#include "yrmainwindow.h"
#include "ui_yrmainwindow.h"
#define CONFIGPATH (QCoreApplication::applicationDirPath() + "fsconfig.ini") //设置一个用于存储的ini文件

QMap<QString, QString> csvBaseMap;
QMap<QString, QString> csvColumnNameMap;

QMap<QString, QString> CSV_TO_DB_COLUMN_MAP;
QMap<QString, QString> Red_CSV_TO_DB_COLUMN_MAP;

QMap<QString, QString> BASE_CSV_TO_DB_COLUMN_MAP;
QMap<QString, QString> DATA_CSV_TO_DB_COLUMN_MAP;

int lineNumber = 0; // 全局变量记录当前曲线数量

bool firstShow = true;

QString nowTestID = "";

QString globalDirPath;

YRMainWindow::YRMainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::YRMainWindow)
{
    ui->setupUi(this);

    // 数据库连接
    // 不使用指针
    yrDatabase = QSqlDatabase::addDatabase("QMYSQL", "myUniqueConnectionName");//创建数据库对象

    // 读取配置文件中的IP地址
    QSettings Isetting(CONFIGPATH, QSettings::IniFormat);
    Isetting.setIniCodec(QTextCodec::codecForName("UTF-8"));
    QString configIP = Isetting.value("settings/ip").toString().trimmed();

    QString finalIP = "127.0.0.1"; // 默认IP
    bool useConfigIP = false;

    // 如果配置文件中有IP地址，尝试连接
    if (!configIP.isEmpty()) {
        // 简单的IP地址格式验证
        QRegularExpression ipRegex("^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$");
        if (ipRegex.match(configIP).hasMatch()) {
            // 创建临时数据库连接测试配置的IP
            QSqlDatabase testDB = QSqlDatabase::addDatabase("QMYSQL", "testConnection");
            testDB.setHostName(configIP);
            testDB.setPort(3306);
            testDB.setDatabaseName("yrsy");
            testDB.setUserName("root");
            testDB.setPassword("123456");
            testDB.setConnectOptions("MYSQL_CHARSET=utf8mb4");

            if (testDB.open()) {
                finalIP = configIP;
                useConfigIP = true;
                testDB.close();
                QMessageBox::information(this, "数据库连接", QString("成功连接到配置的IP: %1").arg(configIP));
            } else {
                qWarning() << "配置的IP连接失败:" << configIP << "- 错误:" << testDB.lastError().text();
            }

            // 移除临时测试连接
            QSqlDatabase::removeDatabase("testConnection");
        } else {
            qWarning() << "配置的IP格式无效:" << configIP;
        }
    }

    yrDatabase.setHostName(finalIP); //为本机的 IP
    yrDatabase.setPort(3306); //端口号，一般数据库都为 3306
    yrDatabase.setDatabaseName("yrsy"); //自己设的数据库名
    yrDatabase.setUserName("root"); //登录用户名 在创建数据库时设置的用户名和密码
    yrDatabase.setPassword("123456"); //登录密码
    yrDatabase.setConnectOptions("MYSQL_CHARSET=utf8mb4"); // 设置连接字符集为 utf8mb4
    qDebug() << "使用数据库IP:" << finalIP;

    if(!yrDatabase.open())
    {
        QMessageBox::warning(this,"打开MySQL数据库失败",yrDatabase.lastError().text());
    }
    else
    {
        qDebug() << "打开MySQL数据库成功，使用IP:" << finalIP;
        if (useConfigIP) {
            // 如果使用了配置的IP，可以在这里添加额外的提示或日志
        }
    }

    Init();

    // 权限检测
    if (login::userType == "User") {
        // 获取menuBar的指针
        QMenuBar *menuBar = this->menuBar();

        // 假设menu_Import是QMenu的实例，并且你有一个指向它的指针
        QMenu *menuImport = this->findChild<QMenu*>("menu_Import");

        if (menuImport) {
            // 获取menuImport的QAction，通常QMenu的QAction是其自己
            QAction *importAction = menuImport->menuAction();

            // 从menuBar中移除该QAction，这将移除整个QMenu
            menuBar->removeAction(importAction);
        }

        // 假设menu_Setting是QMenu的实例，并且你有一个指向它的指针
        QMenu *menuSetting = this->findChild<QMenu*>("menu_setting");

        if (menuSetting) {
            // 获取menuSetting的QAction，通常QMenu的QAction是其自己
            QAction *settingAction = menuSetting->menuAction();

            // 从menuBar中移除该QAction，这将移除整个QMenu
            menuBar->removeAction(settingAction);
        }
    }

    connect(ui->treeWidget, &QTreeWidget::itemClicked, this, &YRMainWindow::onTestIDClicked);
    connect(ui->treeWidget, &QTreeWidget::itemClicked, this, &YRMainWindow::onTestIDClickedToDataDisplay);
    connect(ui->treeWidget, &QTreeWidget::itemClicked, this, &YRMainWindow::onTestIDClickedToMainColumnDisplay);

    // 连接QCustomPlot的信号，selectionChangedByUser表明是由鼠标点击进行的选择
    // 这里主要就是同步图表和图例的显示
    connect(ui->customPlot, &QCustomPlot::selectionChangedByUser, this,&YRMainWindow::selectedShow);
    connect(ui->customPlot, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenuRequest(QPoint)));

    // 设置 tableView 允许自定义右键菜单
    ui->tableView->setContextMenuPolicy(Qt::CustomContextMenu);
    connect(ui->tableView, &QTableView::customContextMenuRequested,this, &YRMainWindow::showTableContextMenu);

    // 设置 tableView_data 允许自定义右键菜单
    ui->tableView_data->setContextMenuPolicy(Qt::CustomContextMenu);
    connect(ui->tableView_data, &QTableView::customContextMenuRequested,this, &YRMainWindow::showTableDataContextMenu);

    // 连接鼠标移动事件到槽函数
    connect(ui->customPlot, &QCustomPlot::mouseMove, this, &YRMainWindow::showPointToolTip);

    // 检查数据库是否存在这些表，没有就创建
    checkAndCreateTable(&yrDatabase);// 传递对象的引用

    // 填充所有TestID到treeWidget
    loadTestIDsToTreeWidget("SELECT DISTINCT TestID FROM TestInfo ORDER BY TestID");

    update_count();

    QSettings setting(CONFIGPATH, QSettings::IniFormat); // 打开存放登录信息的ini文件
    setting.setIniCodec(QTextCodec::codecForName("UTF-8")); // 设置编码格式

    // 读取全局变量 fs 的文件夹路径
    globalDirPath = setting.value("settings/fsdirpath").toString();
    qDebug() << "读取的文件夹路径:" << globalDirPath;

    // 检查是否存在 UTF-8 转义字符（如 \xE7 等），并尝试修正
    if (globalDirPath.contains("\\u")) {
        // 转换类似 \xE7 的转义字符为实际中文
        QByteArray rawBytes = QByteArray::fromPercentEncoding(globalDirPath.toUtf8());
        globalDirPath = QString::fromUtf8(rawBytes);

        // 更新配置文件中的值为修正后的路径
        setting.setValue("settings/fsdirpath", globalDirPath);
        qDebug() << "修正后的文件夹路径:" << globalDirPath;
    }

    if (globalDirPath.isEmpty()) {
        if (login::userType != "User"){
            QMessageBox::information(this, tr("文件夹路径为空"), tr("请配置文件夹路径！"));
        }
    }
}

YRMainWindow::~YRMainWindow()
{
    // 移除数据库连接
    yrDatabase.close(); // 关闭数据库连接
    QSqlDatabase::removeDatabase(yrDatabase.connectionName()); // 移除数据库连接

    foreach (QWidget *widget, findChildren<QDialog*>()) {
        widget->close(); // 触发 WA_DeleteOnClose
    }

    delete ui;
}

// 指针转发声明（PIMPL模式）
void YRMainWindow::closeEvent(QCloseEvent *event) {
    if(dialog) dialog->close();
    QMainWindow::closeEvent(event);
}

void YRMainWindow::Init() {

    model = new QStandardItemModel();//QStandardItemModel 是包含单元格的容器（在这里可以看作表）

    data_model = new QStandardItemModel();

    QList<QPair<QString, QString>> csvBaseList({
        {"Test", "试验"},
        {"General information", "基本信息"},
        {"ConeCalc 6 iCone Test Data", "锥形量热仪iCone试验数据"},
                                                   {"Cone Calorimeter Test Report", "锥形量热仪试验报告"},
                                                   {"Standard used", "采用标准"},
                                                   {"Date of report", "报告日期"},
                                                   {"Date of test", "试验日期"},
                                                   {"Time of test", "试验时间"},
                                                   {"Heat flux", "辐射热流强度(kW/㎡)"},
                                                   {"Nominal duct flow rate", "排烟管道流量(l/s)"},
                                                   {"Sampling interval", "采样时间间隔(s)"},
                                                   {"Separation", "距点火器距离(mm)"},
                                                   {"Orientation", "辐射方向"},
                                                   {"Edge frame?", "是否有边框？"},
                                                   {"Edge frame used?", "是否有边框？"},
                                                   {"Grid?", "是否加网格？"},
                                                   {"Grid used?", "是否加网格？"},
                                                   {"Non-scrubbed?", "是否未洗气？"},
                                                   {"Substrate used? {Y/N}", "使用基底？[是/否]"},
                                                   {"Substrate", "基底"},
                                                   {"Additional specimen preparation details", "其它试样制备细节"},
        {"Specimen", "样品"},
                                                   {"Sample description", "样品描述"},
                                                   {"Specimen information", "试样信息"},
                                                   {"Material name/ID", "材料名称/ID"},
                                                   {"Material name", "材料名称"},
                                                   {"Specimen number", "试样编号"},
                                                   {"E", "消耗每公斤氧气的热释放 E(MJ/kg)"},
                                                   {"Initial mass", "初始质量(g)"},
                                                   {"Thickness", "厚度 (mm)"},
                                                   {"Surface area", "表面积 (c㎡)"},
                                                   {"Sponsor", "负责人"},
                                                   {"Manufacturer", "生产企业"},
                                                   {"Test times", "试验时间"},
                                                   {"Test start time", "试验开始时间(s)"},
                                                   {"Time to ignition", "引燃时间(s)"},
                                                   {"Ignition to flameout", "持续燃烧时间(s)"},
                                                   {"Time to flameout", "熄灭时间(s)"},
                                                   {"End of test criterion", "试验结束标准"},
                                                   {"User EOT time", "试验结束时间(s)"},
                                                   {"MLR EOT mass", "结束时计算质量损失速率的单位面积质量损失(g/㎡)"},
                                                   {"MLR EOT time period", "结束时计算质量损失速率的时间间隔(s)"},
                                                   {"Truncate graphs at EOT?", "试验结束时截图？"},
                                                   {"Smooth data?", "是否平滑结果曲线？"},
                                                   {"Correct O2 for pressure?", "采样气压修正氧气读数？"},

        {"Specifications: apparatus", "设备技术参数"},
                                                   {"Apparatus specifications", "设备技术参数"},
                                                   {"C-factor", "C-因子(SI 单元)"},
                                                   {"OD correction factor", "OD修正因子(s)"},
                                                   {"Duct diameter", "导管直径(m)"},
                                                   {"O2 delay time", "O2延迟时间(s)"},
                                                   {"CO2 delay time", "CO2延迟时间(s)"},
                                                   {"CO delay time", "CO延迟时间(s)"},
                                                   {"OD corr. Factor", "OD修正因子"},
        {"Initial conditions", "初始条件"},
                                                   {"Baseline ambient oxygen", "基准环境氧含量(%)"},
                                                   {"Baseline oxygen", "基准氧含量(%)"},
                                                   {"Baseline carbon dioxide", "基准二氧化碳含量(%)"},
                                                   {"Mass at sustained flaming", "进入稳定燃烧时的质量(g)"},
                                                   {"time to 70% mass loss", "质量损失达70%的时间(s)"},
        {"Laboratory", "实验室"},
                                                   {"Laboratory name", "实验室名称"},
                                                   {"Laboratory address", "实验室地址"},
                                                   {"Test commissioned  address", "试验委托地址"},
                                                   {"Test commissioned  name", "试验委托单位"},
                                                   {"Test commissioned address", "试验委托单位"},
                                                   {"Manufacturers name", "生产企业名称"},
                                                   {"Manufacturers address", "生产企业地址"},
                                                   {"Operator", "操作员"},
                                                   {"Filename", "文件名"},
                                                   {"Report name", "报告名"},
        {"Pre-test conditions", "试验前环境状态"},
                                                   {"Ambient temperature", "环境温度(℃)"},
                                                   {"Ambient Pressure", "环境温度(℃)"},
                                                   {"Barometric pressure", "环境压力(Pa)"},
                                                   {"Relative humidity", "环境相对湿度 (%)"},
        {"Conditioning", "环境状态调节"},
                                                   {"Conditioned? {Y/N}", "是否状态调节过？[是/否]"},
                                                   {"Conditioned?", "是否状态调节过？[是/否]"},
                                                   {"Conditioning temperature", "温度调节(℃)"},
                                                   {"Temperature", "温度调节(℃)"},
                                                   {"Conditioning RH", "相对湿度调节(%)"},
                                                   {"Rel. Humidity", "相对湿度调节(%)"},
        {"Data collected", "数据采集"},
                                                   {"CO/CO2 data collected?", "CO/CO2数据采集？"},
                                                   {"Mass data collected?", "质量数据采集？"},
                                                   {"Smoke data collected?", "烟气数据采集？"},
                                                   {"Soot mass data collected?", "烟灰重量数据采集？"},
                                                   {"Soot mass ratio", "烟灰重量比率(1:x)"},
                                                   {"Soot mass", "烟灰重量 (g)"},
        {"Comments", "说明"},
                                                   {"comments", "说明"},
                                                   {"Pre-test comments", "试验前说明"},
                                                   {"After-test comments", "试验后说明"},
                                                   {"Heat Release Results", "热释放速率结果"},
                                                   {"Fuel load", "燃料火灾荷载"},
                                                   {"", ""},
        {"Exported data", "输出数据"},
        {"Parameter", "参数"},
        {"Peaks", "峰值"},
                                                   {"Peak", "峰值"},
        {"Time To Peaks", "达到峰值的时间"},
        {"Test Results", "试验结果"},
                                                   {"Test results", "试验结果"},
                                                   {"Mean", "平均值"},
                                                   {"mean", "平均值"},
        {"Test Averages", "测试平均值"},
                                                   {"Test averages", "试验平均值"},
                                                   {"Smoke results", "烟气结果"},
        {"User-defined Test Averages", "用户定义的测试平均值"},
                                                   {"Risk level", "危险分级"},
                                                   {"Material type", "材料类型"},
                                                   {"Ignition method", "引燃方法"},
                                                   {"Test equipment name", "试验设备名"},
                                                   {"Test equipment photo", "试验设备照片"},
                                                   {"Sample photo", "样品照片"},
                                                   {"Specimen photo", "试样照片"},
                                                   {"Specimen photo before", "试验前试样照片"},
                                                   {"Specimen photo after", "试验后试样照片"},
                                                   {"Test video", "试验录像"},
                                                   {"Report file", "报告文件"},
                                                   {"Note", "备注"},
                                                   {"note", "备注"},
                                                   {"Total heat release", "总热释放量(MJ/m²)"},
                                                   {"Total oxygen consumed", "总耗氧量(g)"},
                                                   {"Mass lost", "单位面积的质量损失(g/m²)"},
                                                   {"Mass lost per unit area", "单位面积的质量损失(g/m²)"},
                                                   {"Total smoke release", "单位面积总烟气释放量(m²/m²)"},
                                                   {"Total smoke production", "总产烟量(m²)"},
                                                   {"MLR per unit area", "单位面积质量损失速率(g/(s•㎡))"},
                                                   {"Average specific MLR", "单位面积质量损失速率(g/(s•㎡))"},
                                                   {"MARHE", "最大平均热释放速率 (kW/㎡)"}
    });

    QList<QPair<QString, QString>> CSV_List = {
        // 基础测量数据列映射
        {"time", "Time"},
        {"Time", "Time"},
        {"O2", "O2"},
        {"DPT", "DPT"},
        {"CO", "CO"},
        {"CO2", "CO2"},
        {"PDM", "PDM"},
        {"PDC", "PDC"},
        {"Mass", "Mass"},
        {"Methane MFM", "MethaneMFM"},
        {"MethaneMFM", "MethaneMFM"},
        {"APT", "APT"},
        {"HFM", "HFM"},
        {"Cold Trap TC", "ColdTrapTC"},
        {"ColdTrapTC", "ColdTrapTC"},
        {"Stack TC", "StackTC"},
        {"StackTC", "StackTC"},
        {"Smoke TC", "SmokeTC"},
        {"SmokeTC", "SmokeTC"},
        {"Cone Heater TC", "ConeHeaterTC"},
        {"ConeHeaterTC", "ConeHeaterTC"},
        {"Spare TC", "SpareTC"},
        {"SpareTC", "SpareTC"},
        {"Soot MFM", "SootMFM"},
        {"SootMFM", "SootMFM"},
        {"AFM", "AFM"}
    };

    QList<QPair<QString, QString>> Red_CSV_List = {
        // 红色文件特有列映射
        {"time", "Time"},
        {"Time", "Time"},
        {"HRR", "HRR"},
        {"EHC", "EHC"},
        {"SEA", "SEA"},
        {"MLR", "MLR"},
        {"Specific MLR", "SpecificMLR"},
        {"SpecificMLR", "SpecificMLR"},
        {"COY", "COY"},
        {"CO2Y", "CO2Y"},
        {"THR", "THR"},
        {"MFR", "MFR"},
        {"k", "k"},
        {"V Stack", "VStack"},
        {"V stack", "VStack"},
        {"V Smoke", "VSmoke"},
        {"V smoke", "VSmoke"},
        {"SPR", "SPR"},
        {"RSR", "RSR"},
        {"TSP", "TSP"},
        {"TSR", "TSR"},
        {"COP", "COP"},
        {"CO2P", "CO2P"},
        {"O2C", "O2C"},
        {"TOC", "TOC"},
        {"ARHE", "ARHE"},
        {"Specimen mass", "SpecimenMass"},

        // 重复测量项
        {"Oxygen", "Oxygen"},
        {"DPT", "DPT_"},
        {"CO", "CO_"},
        {"CO2", "CO2_"},
        {"PDM", "PDM_"},
        {"PDC", "PDC_"},
        {"APT", "APT_"},
        {"Cold Trap TC", "ColdTrapTC_"},
        {"Stack TC", "StackTC_"},
        {"Smoke TC", "SmokeTC_"},
        {"Cone Heater TC", "ConeHeaterTC_"},
        {"Soot MFM", "SootMFM_"},
        {"Spare TC", "SpareTC_"},
        {"HFM", "HFM_"}
    };

    QList<QPair<QString, QString>> Base_CSV_List = {
        {"Time", "Time"},
        {"HRR", "HRR"},
        {"Smoke production rate", "SPR"},
        {"Total heat release", "THR"},
        {"Total smoke production", "TSP"},
        {"Oxygen", "Oxygen"},
        {"Carbon monoxide", "CO_"},
        {"Carbon dioxide", "CO2_"},
        {"Specimen mass", "SpecimenMass"},
        {"DPT", "DPT_"},
        {"Cold Trap TC", "ColdTrapTC_"},
        {"Stack TC", "StackTC_"},
        {"Smoke TC", "SmokeTC_"},
        {"Cone Heater TC", "ConeHeaterTC_"},
        {"V Stack", "VStack"},
        {"COP", "COP"},
        {"CO2P", "CO2P"},
        {"COY", "COY"},
        {"CO2Y", "CO2Y"},
        {"EHC", "EHC"}
    };

    QList<QPair<QString, QString>> Data_CSV_List = {
        {"Time", "Time"},
        {"HRR35", "HRR"},
        {"SPR35", "SPR"},
        {"THR", "THR"},
        {"TSP", "TSP"},
        {"MLR", "MLR"},
        {"Ext.coff.", "k"},
        {"SEA", "SEA"},
        {"ARHE", "ARHE"},
        {"O2C", "O2C"},
        {"TSR", "TSR"},
        {"RSR", "RSR"}
    };

    // 初始化中英文列名
    QList<QPair<QString, QString>> columnNameList({
        {"id", "ID"},
        {"TestID", "试验编号"},
        {"Time", "时间(s)"},
        {"O2", "氧气(%)"},
        {"DPT", "孔板处压差(Pa)"},
        {"CO", "一氧化碳(%)"},
        {"CO2", "二氧化碳(%)"},
        {"PDM", "主感光器(-)"},
        {"PDC", "补偿感光器(-)"},
        {"Mass", "试样质量(g)"},
        {"MethaneMFM", "甲烷质量流速(slpm)"},
        {"APT", "环境大气压(kPa)"},
        {"HFM", "热流量(kW/m²)"},
        {"ColdTrapTC", "冷阱温度(K)"},
        {"StackTC", "孔板处烟气温度(K)"},
        {"SmokeTC", "管道内烟气温度(K)"},
        {"ConeHeaterTC", "锥形加热器温度(K)"},
        {"SpareTC", "备用热电偶测得温度(K)"},
        {"SootMFM", "烟灰质量流速(g/s)"},
        {"AFM", "采样气流量(l/min)"},
        {"HRR", "热释放率(kW/㎡)"},
        {"EHC", "有效燃烧热(MJ/kg)"},
        {"SEA", "比消光面积(m²/kg)"},
        {"MLR", "质量损失速率(g/s)"},
        {"SpecificMLR", "单位面积质量损失速率(g/(s·㎡))"},
        {"COY", "CO生成量(kg/kg)"},
        {"CO2Y", "CO2生成量(kg/kg)"},
        {"THR", "总热释放(MJ/㎡)"},
        {"MFR", "质量流量(g/s)"},
        {"k", "比消光系数(l/m)"},
        {"VStack", "孔板处烟气体积流速(l/s)"},
        {"VSmoke", "管道内烟气体积流速(l/s)"},
        {"SPR", "产烟速率 (㎡/s)"},
        {"RSR", "单位面积产烟速率((㎡/s)/㎡)"},
        {"TSP", "总产烟量(㎡)"},
        {"TSR", "单位面积总产烟量(㎡/㎡)"},
        {"COP", "CO生成速率(g/s)"},
        {"CO2P", "CO2生成速率(g/s)"},
        {"O2C", "耗氧速率(g/s)"},
        {"TOC", "总耗氧量(g)"},
        {"ARHE", "平均热释放速率(kW/㎡)"},
        {"SpecimenMass", "样品质量(g)"},
        {"Oxygen", "空气氧浓度(%)"},
        {"DPT_", "孔板处压差(Pa)"},
        {"CO_", "空气CO浓度(%)"},
        {"CO2_", "空气CO2浓度(%)"},
        {"PDM_", "主感光器(-)"},
        {"PDC_", "补偿感光器(-)"},
        {"APT_", "环境大气压(kPa)"},
        {"ColdTrapTC_", "冷阱温度(K)"},
        {"StackTC_", "孔板处烟气温度(K)"},
        {"SmokeTC_", "管道内烟气温度(K)"},
        {"ConeHeaterTC_", "锥形加热器温度(K)"},
        {"SootMFM_", "烟灰质量流速(g/s)"},
        {"SpareTC_", "备用热电偶测得温度(K)"},
        {"HFM_", "热流量(kW/㎡)"},
    });

    for (const QPair<QString, QString>& pair : CSV_List) {
        CSV_TO_DB_COLUMN_MAP.insert(pair.first, pair.second);
    }

    for (const QPair<QString, QString>& pair : Red_CSV_List) {
        Red_CSV_TO_DB_COLUMN_MAP.insert(pair.first, pair.second);
    }

    for (const QPair<QString, QString>& pair : Base_CSV_List) {
        BASE_CSV_TO_DB_COLUMN_MAP.insert(pair.first, pair.second);
    }

    for (const QPair<QString, QString>& pair : Data_CSV_List) {
        DATA_CSV_TO_DB_COLUMN_MAP.insert(pair.first, pair.second);
    }

    for (const QPair<QString, QString>& pair : csvBaseList) {
        csvBaseMap.insert(pair.first, pair.second);
    }

    for (const QPair<QString, QString>& pair : columnNameList) {
        csvColumnNameMap.insert(pair.first, pair.second);
    }

    ui->customPlot->setMultiSelectModifier(Qt::ControlModifier);// 使用ctrl键来多选
    ui->customPlot->setContextMenuPolicy(Qt::CustomContextMenu);//自定义菜单
}


// 检查并创建试验基本信息表（Info与Results），用于存储各csv文件前两列数据
// 检查并创建试验详细数据表（Data），用于存储各csv文件剩余列数据
bool YRMainWindow::checkAndCreateTable(QSqlDatabase *yrDatabase) {
    if (!yrDatabase || !yrDatabase->isOpen()) {
        qWarning() << "数据库连接无效或未打开";
        return false;
    }

    try {
        // 检查 TestInfo 表是否存在
        QSqlQuery checkTable(*yrDatabase);
        if (!checkTable.exec("SHOW TABLES LIKE 'TestInfo'")) {
            qWarning() << "SHOW TABLES LIKE 查询失败: " << checkTable.lastError().text();
            return false;
        }

        if (!checkTable.next()) {
            // TestInfo 表不存在，创建表
            QSqlQuery createTable(*yrDatabase);
            QString createTestInfoSQL = R"(
                                        CREATE TABLE IF NOT EXISTS TestInfo (
                                        id INT AUTO_INCREMENT PRIMARY KEY,
                                        TestID VARCHAR(20) NOT NULL,
                                        InfoName VARCHAR(255) NOT NULL,
                                        InfoValue TEXT
                                        );
                                        )";

            if (!createTable.exec(createTestInfoSQL)) {
                qWarning() << "创建 TestInfo 表失败: " << createTable.lastError().text();
                return false;
            }
        }

        // 检查 TestResults 表是否存在
        if (!checkTable.exec("SHOW TABLES LIKE 'TestResults'")) {
            qWarning() << "SHOW TABLES LIKE 查询失败: " << checkTable.lastError().text();
            return false;
        }

        if (!checkTable.next()) {
            // TestResults 表不存在，创建表
            QSqlQuery createTable(*yrDatabase);
            QString createTestResultsSQL = R"(
                                           CREATE TABLE IF NOT EXISTS TestResults (
                                           id INT AUTO_INCREMENT PRIMARY KEY,
                                           TestID VARCHAR(20) NOT NULL,
                                           ResultName VARCHAR(255) NOT NULL,
                                           ResultValue TEXT
                                           );
                                           )";

            if (!createTable.exec(createTestResultsSQL)) {
                qWarning() << "创建 TestResults 表失败: " << createTable.lastError().text();
                return false;
            }
        }

        // 检查 TestData 表是否存在
        if (!checkTable.exec("SHOW TABLES LIKE 'TestData'")) {
            qWarning() << "SHOW TABLES LIKE 查询失败: " << checkTable.lastError().text();
            return false;
        }

        if (!checkTable.next()) {
            // TestData 表不存在，创建表
            QSqlQuery createTable(*yrDatabase);
            QString createTestDataSQL = R"(
                                        CREATE TABLE IF NOT EXISTS TestData (
                                        id INT AUTO_INCREMENT PRIMARY KEY,
                                        TestID VARCHAR(20) NOT NULL,
                                        Time FLOAT NOT NULL,  -- Time in seconds (5s intervals)

                                        /* Basic measurement columns */
                                        O2 VARCHAR(30),                  -- Oxygen percentage
                                        DPT VARCHAR(30),                 -- Differential Pressure Transducer (Pa)
                                        CO VARCHAR(30),                  -- Carbon Monoxide percentage
                                        CO2 VARCHAR(30),                 -- Carbon Dioxide percentage
                                        PDM VARCHAR(30),                 -- Pressure Drop Multiplier
                                        PDC VARCHAR(30),                 -- Pressure Drop Constant
                                        Mass VARCHAR(30),                -- Mass (g)
                                        MethaneMFM VARCHAR(30),          -- Methane Mass Flow Meter (slpm)
                                        APT VARCHAR(30),                 -- Ambient Pressure Transducer (kPa)
                                        HFM VARCHAR(30),                 -- Heat Flux Meter (kW/m²)
                                        ColdTrapTC VARCHAR(30),          -- Cold Trap Thermocouple (K)
                                        StackTC VARCHAR(30),             -- Stack Thermocouple (K)
                                        SmokeTC VARCHAR(30),             -- Smoke Thermocouple (K)
                                        ConeHeaterTC VARCHAR(30),        -- Cone Heater Thermocouple (K)
                                        SpareTC VARCHAR(30),             -- Spare Thermocouple (K)
                                        SootMFM VARCHAR(30),             -- Soot Mass Flow Meter (g/s)
                                        AFM VARCHAR(30),                 -- Air Flow Meter (l/min)

                                        /* Analysis columns */
                                        HRR VARCHAR(30),                 -- Heat Release Rate (kW/m²)
                                        EHC VARCHAR(30),                 -- Effective Heat of Combustion (MJ/kg)
                                        SEA VARCHAR(30),                 -- Specific Extinction Area (m²/kg)
                                        MLR VARCHAR(30),                 -- Mass Loss Rate (g/s)
                                        SpecificMLR VARCHAR(30),         -- Specific Mass Loss Rate (g/s·m²)
                                        COY VARCHAR(30),                 -- CO Yield (kg/kg)
                                        CO2Y VARCHAR(30),                -- CO2 Yield (kg/kg)
                                        THR VARCHAR(30),                 -- Total Heat Released (MJ/m²)
                                        MFR VARCHAR(30),                 -- Mass Flow Rate (g/s)
                                        k VARCHAR(30),                   -- Extinction Coefficient (1/m)
                                        VStack VARCHAR(30),              -- Volume Stack (l/s)
                                        VSmoke VARCHAR(30),              -- Volume Smoke (l/s)
                                        SPR VARCHAR(30),                 -- Smoke Production Rate (m²/s)
                                        RSR VARCHAR(30),                 -- Rate of Smoke Release ((m²/s)/m²)
                                        TSP VARCHAR(30),                 -- Total Smoke Production (m²)
                                        TSR VARCHAR(30),                 -- Total Smoke Released (m²/m²)
                                        COP VARCHAR(30),                 -- CO Production (g/s)
                                        CO2P VARCHAR(30),                -- CO2 Production (g/s)
                                        O2C VARCHAR(30),                 -- O2 Consumption (g/s)
                                        TOC VARCHAR(30),                 -- Total O2 Consumed (g)
                                        ARHE VARCHAR(30),                -- Accumulated Rate of Heat Emission (kW/m²)
                                        SpecimenMass VARCHAR(30),        -- Specimen mass (g)

                                        /* Additional measurement columns */
                                        Oxygen VARCHAR(30),          -- Oxygen percentage (duplicated but kept for completeness)
                                        DPT_ VARCHAR(30),                -- Second DPT measurement (Pa)
                                        CO_ VARCHAR(30),                 -- CO percentage (duplicated)
                                        CO2_ VARCHAR(30),               -- Second CO2 percentage
                                        PDM_ VARCHAR(30),                -- Second PDM measurement
                                        PDC_ VARCHAR(30),                -- Second PDC measurement
                                        APT_ VARCHAR(30),                -- Second APT measurement (kPa)
                                        ColdTrapTC_ VARCHAR(30),         -- Second Cold Trap TC (K)
                                        StackTC_ VARCHAR(30),            -- Second Stack TC (K)
                                        SmokeTC_ VARCHAR(30),            -- Second Smoke TC (K)
                                        ConeHeaterTC_ VARCHAR(30),       -- Second Cone Heater TC (K)
                                        SootMFM_ VARCHAR(30),            -- Second Soot MFM (g/s)
                                        SpareTC_ VARCHAR(30),            -- Second Spare TC (K)
                                        HFM_ VARCHAR(30),                -- Second HFM (kW/m²)

                                        /* Indexing */
                                        INDEX idx_test_time (TestID, Time),
                                        INDEX idx_testid (TestID)
                                        );
                                        )";

            if (!createTable.exec(createTestDataSQL)) {
                qWarning() << "创建 TestData 表失败: " << createTable.lastError().text();
                return false;
            }
        }

        return true; // 表已存在或创建成功
    } catch (const std::exception &e) {
        qCritical() << "在 checkAndCreateTable 中发生异常: " << e.what();
        return false;
    } catch (...) {
        qCritical() << "在 checkAndCreateTable 中发生未知异常";
        return false;
    }
}

bool YRMainWindow::importCSVToDatabase(const QString &filePath, QSqlDatabase *yrDatabase, bool isRedFile) {
    // 检查数据库连接
    if (!yrDatabase || !yrDatabase->isOpen()) {
        qWarning() << "数据库未连接或无效";
        return false;
    }

    QFile file(filePath);
    if (!file.open(QIODevice::ReadOnly  | QIODevice::Text)) {
        qWarning() << "无法打开文件:" << filePath;
        return false;
    }

    // 开启事务
    if (!yrDatabase->transaction()) {
        qWarning() << "事务启动失败:" << yrDatabase->lastError().text();
        file.close();
        return false;
    }

    QTextStream stream(&file);
    QSqlQuery query(*yrDatabase);

    // 提取TestID（保留原逻辑）
    QString testId = extractTestIdFromFileName(filePath);
    if (testId.isEmpty())  {
        qWarning() << "无效的TestID";
        file.close();
        yrDatabase->rollback();
        return false;
    }

    // 参数化SQL模板
    const QString insertTemplate =
        "INSERT INTO %1 (TestID, %2Name, %2Value) VALUES (:testId, :name, :value)";
    query.prepare(insertTemplate
        .arg(isRedFile ? "TestResults" : "TestInfo")
        .arg(isRedFile ? "Result" : "Info"));

    QMap<QString, QString> replacements {
            {"\\Surface area \\(cm\\s*\\b", "Surface area (c㎡)"},
            {"\\(kW/m\\s*\\b", "(kW/㎡)"},
            {"\\(\\?C\\)", "(℃)"},  // 捕捉潜在的乱码字符并替换
            {"\\(�C\\)", "(℃)"},    // 匹配并替换已知的乱码字符
            {"\\(g/m\\s*\\b","(g/㎡)"},
            {"\\(mkg\\)","(㎡/kg)"},  // 替换red乱码
            {"Total heat release \\(MJ/m","Total heat release (MJ/㎡)"},
            {"Average specific MLR \\(g/s[^a-zA-Z0-9)]*\\)", "Average specific MLR (g/(s·㎡))"},
            //{"Average specific MLR \\(g/s�m","Average specific MLR (g/(s·㎡))"},
            {"Total smoke release \\(mm\\s*\\b","Total smoke release (㎡/㎡)"},
            {"Total smoke production \\(m\\s*\\b","Total smoke production (㎡)"},
            {"Conditioning temperature \\(癈\\)","Conditioning temperature (℃)"},
            {"Ambient temperature \\(癈\\)","Ambient temperature (℃)"}
            // ... 添加更多的乱码和替换字符串对
        //{"Average specific MLR \\(g/s\\s*\\b","Average specific MLR (g/(s·㎡))"},
        };

    int time_start, time_end = 0;
    bool flag = false;

    while (!stream.atEnd())  {
        QString line = stream.readLine();
        QStringList fields = line.split(",",  QString::KeepEmptyParts);

        // 跳过无效行
        if (fields.size()  < 2 || fields[0].trimmed().isEmpty()) continue;

        // 替换可能的乱码字段名
        for (const auto &key : replacements.keys()) {
            fields[0].replace(QRegularExpression(key), replacements[key]);
        }

        // 映射字段值（保留原逻辑）
//        QMapIterator<QString, QString> i(csvBaseMap);
//        while (i.hasNext())  {
//            i.next();
//            // 填充二级标题的中文字段 i.key().length() == fields[0].length() &&
//            if (i.key() == fields[0].left(i.key().length()))  {
//                fields[0] = i.value();
//            }
//            // 该单位单独适配
//            if (fields[0].contains("Average specific MLR")) {
//                fields[0] = "Average specific MLR (g/(s·㎡))";
//            }
//        }


        if (fields[0].contains("Average specific MLR")) {
            fields[0] = "Average specific MLR (g/(s·㎡))";
        }

        // 处理字段值，提取数字部分
        if (!fields[1].isEmpty()) {
            // 新增：如果字段名是 "Report file"，则跳过所有处理，保留原始值
            if (fields[0] == "Report file" || fields[0].contains("Comments", Qt::CaseInsensitive) || fields[0].contains("photo", Qt::CaseInsensitive) || fields[0].contains("video", Qt::CaseInsensitive)) {
                // 不做任何修改，保留原始路径
            } else {
                // 检查是否为日期格式或其他应该保留的特殊格式
                QRegularExpression dateRe("^\\d{1,2}/\\d{1,2}/\\d{4}$"); // 改为常见日期格式
                QRegularExpression timeRe("^\\d{1,2}:\\d{1,2}(?::\\d{1,2})?$"); // 支持 HH:MM 或 HH:MM:SS 格式

                if (dateRe.match(fields[1]).hasMatch() ||
                        timeRe.match(fields[1]).hasMatch() ||
                        fields[1] == "Yes" || fields[1] == "No" ||
                        fields[1] == "N/A" || fields[1] == "--") {
                    // 如果是特殊格式，保留原样
                    // 不做任何修改
                } else {
                    // 使用正则表达式匹配数字部分（包括小数点）
                    QRegularExpression re("^([0-9.]+)");
                    QRegularExpressionMatch match = re.match(fields[1]);
                    if (match.hasMatch()) {
                        // 只保留匹配到的数字部分
                        fields[1] = match.captured(1);
                    }
                }
            }
        }

        // 引燃时间或熄灭时间，为空或小于等于零，赋值为0，表示未引燃
        if (fields[0].contains("Time to ignition")) {
            if (fields[1].toInt() <= 0) {
                fields[1] = "0";
                time_start = 0;
            } else {
                time_start = fields[1].toInt();
            }
        }

        if (fields[0].contains("Ignition to flameout")) flag = true;

        if (fields[0].contains("Time to flameout")) {
            if (fields[1].toInt() <= 0) {
                fields[1] = "0";
                time_end = 0;
            } else {
                time_end = fields[1].toInt();
            }
            // 点燃时间和熄灭时间之间添加持续时间，当Ignition to flameout不存在时才插入
            if (!flag) {
                query.bindValue(":testId",  testId);
                query.bindValue(":name",  "Ignition to flameout");
                query.bindValue(":value",  QString::number(time_end - time_start));
                query.exec();
            }

        }

        // 绑定参数
        query.bindValue(":testId",  testId);
        query.bindValue(":name",  fields[0]);
        query.bindValue(":value",  fields[1]);

        if (!query.exec())  {
            qWarning() << "插入失败:" << query.lastError().text()
                       << "\nSQL:" << query.lastQuery();
            file.close();
            yrDatabase->rollback();
            return false;
        }
    }

    // 提交事务
    if (!yrDatabase->commit()) {
        qWarning() << "事务提交失败:" << yrDatabase->lastError().text();
        yrDatabase->rollback();
        file.close();
        return false;
    }

    file.close();
    return true;
}

// 导入数据列
bool YRMainWindow::importDetailedData(const QString &baseFilePath, const QString &redFilePath, QSqlDatabase *yrDatabase) {
    // 检查数据库连接
    if (!yrDatabase || !yrDatabase->isOpen()) {
        qWarning() << "数据库未连接或无效";
        return false;
    }

    QFile baseFile(baseFilePath);
    QFile redFile(redFilePath);

    if (!baseFile.open(QIODevice::ReadOnly | QIODevice::Text) ||
            !redFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qWarning() << "无法打开文件";
        return false;
    }

    // 开启事务
    if (!yrDatabase->transaction()) {
        qWarning() << "事务启动失败:" << yrDatabase->lastError().text();
        baseFile.close();
        redFile.close();
        return false;
    }

    QSqlQuery query(*yrDatabase);

    // 提取TestID
    QString testId = extractTestIdFromFileName(baseFilePath);
    if (testId.isEmpty()) {
        qWarning() << "无效的TestID";
        return false;
    }

    QString insertSQL = "INSERT INTO TestData (TestID, Time, O2, DPT, CO, CO2, PDM, PDC, "
                        "Mass, MethaneMFM, APT, HFM, ColdTrapTC, StackTC, SmokeTC, ConeHeaterTC, SpareTC, "
                        "SootMFM, AFM, HRR, EHC, SEA, MLR, SpecificMLR, COY, CO2Y, THR, MFR, k, VStack, VSmoke, "
                        "SPR, RSR, TSP, TSR, COP, CO2P, O2C, TOC, ARHE, SpecimenMass, Oxygen, DPT_, CO_, CO2_, PDM_, "
                        "PDC_, APT_, ColdTrapTC_, StackTC_, SmokeTC_, ConeHeaterTC_, SootMFM_, SpareTC_, HFM_) VALUES (?, ?, ?, ?, ?, "
                        "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, "
                        "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";

    query.prepare(insertSQL);

    // 构造5秒间隔的时间点序列，提取redFile的Time。即timePoints为Time列的内容
    QList<int> timePoints;
    {
        QFile redFileT(redFilePath);
        if (!redFileT.open(QIODevice::ReadOnly | QIODevice::Text)) {
            qWarning() << "无法打开red文件";
            return false;
        }

        QTextStream redStream(&redFileT);
        bool isFirstLine = true;
        bool isSecondLine = true;
        int timeColumnIndex = -1;

        while (!redStream.atEnd()) {
            QString line = redStream.readLine();
            if (line.trimmed().isEmpty()) continue;

            // Skip the first line "Exported data"
            if (isFirstLine) {
                isFirstLine = false;
                continue;
            }

            // Split by comma (CSV format)
            QStringList values = line.split(',');

            // Process the header line (second line)
            if (isSecondLine) {
                isSecondLine = false;
                timeColumnIndex = values.indexOf("Time (s)");
                if (timeColumnIndex == -1) {
                    qWarning() << "未找到Time (s)列";
                    redFileT.close();
                    return false;
                }
                continue;
            }

            // Process data lines
            if (values.size() > timeColumnIndex) {
                bool ok;
                // Remove any quotes or whitespace from the time value
                QString timeStr = values[timeColumnIndex].trimmed().remove('"');
                int time = timeStr.toInt(&ok);
                if (ok) {
                    timePoints.append(time);
                } else {
                    qWarning() << "无效的时间值:" << timeStr;
                }
            }
        }
        redFileT.close();

        qDebug() << "提取到的时间点数量:" << timePoints.size();
        if (!timePoints.isEmpty()) {
            qDebug() << "时间点示例:" << timePoints.first() << "..." << timePoints.last();
        }
    }


    // 读取两个CSV文件的数据
    QMap<int, QMap<QString, QString>> baseData;  // time -> {column -> value}
    QMap<int, QMap<QString, QString>> redData;   // time -> {column -> value}

    // 读取各CSV第三列开始的后n列数据
    // 读取baseFile（保留第一行作为表头，移除前两列）
    readCSVData(baseFile, baseData, false);
    // 读取redFile（跳过第一行，移除前两列）
    readCSVData(redFile, redData, true);

    // 构建QMap，存储*.CSV与*_red.CSV中符合时间间隔为5s的数据行
    QMap<QString, QString> BaseData;// {column -> value}
    QMap<QString, QString> RedData;// {column -> value}

    // 插入每个5秒间隔的数据点
    for (int timePoint : timePoints) {
        query.bindValue(0, testId);
        query.bindValue(1, timePoint);

        int paramIndex = 2;  // 从第3个参数开始绑定

        BaseData = getLineData(timePoint, baseData);  // 获取指定 timePoint 当行的所有列数据

        for (auto it = BaseData.begin(); it != BaseData.end(); ++it) {
            qDebug() << "BaseData映射表:" << it.key() << " -> " << it.value();
        }

        // 绑定 *.CSV 数据的各列，没有的数据绑定 0.0
        QStringList baseColumns = {
            "O2", "DPT", "CO", "CO2", "PDM", "PDC", "Mass", "MethaneMFM", "APT",
            "HFM", "ColdTrapTC", "StackTC", "SmokeTC", "ConeHeaterTC", "SpareTC",
            "SootMFM", "AFM"
        };

        for (const QString &col : baseColumns) {
            query.bindValue(paramIndex++, BaseData.value(col, ""));
        }

        RedData = getLineData(timePoint, redData);  // 获取指定 timePoint 当行的所有列数据

        for (auto it = RedData.begin(); it != RedData.end(); ++it) {
            qDebug() << "RedData映射表:" << it.key() << " -> " << it.value();
        }

        // 绑定 *_red.CSV 数据的各列
        QStringList redColumns = {
            "HRR", "EHC", "SEA", "MLR", "SpecificMLR", "COY", "CO2Y", "THR", "MFR", "k",
            "VStack", "VSmoke", "SPR", "RSR", "TSP", "TSR", "COP", "CO2P", "O2C", "TOC",
            "ARHE", "SpecimenMass", "Oxygen", "DPT_", "CO_", "CO2_", "PDM_", "PDC_",
            "APT_", "ColdTrapTC_", "StackTC_", "SmokeTC_", "ConeHeaterTC_",
            "SootMFM_", "SpareTC_", "HFM_"
        };

        for (const QString &col : redColumns) {
            query.bindValue(paramIndex++, RedData.value(col, ""));
        }

        qDebug() << "paraIndex: " << paramIndex;

        QString sql = query.lastQuery();
        QMap<QString, QVariant> boundValues = query.boundValues();
        QRegularExpression regex("\\?");
        int pos = 0;

        for (auto it = boundValues.begin(); it != boundValues.end(); ++it) {
            QString valueStr;
            if (it.value().type() == QVariant::String) {
                valueStr = "'" + it.value().toString().replace("'", "''") + "'";
            } else if (it.value().isNull()) {
                valueStr = "NULL";
            } else {
                valueStr = it.value().toString();
            }

            pos = sql.indexOf('?', pos);
            if (pos != -1) {
                sql.replace(pos, 1, valueStr);
                pos += valueStr.length();
            } else {
                break;
            }
        }

        qDebug() << "即将执行的SQL:" << sql;
        qDebug() << "绑定的参数数量:" << query.boundValues().size();

        if (!query.exec()) {
            qWarning() << "插入失败:" << query.lastError().text()
                       << "\nSQL:" << query.lastQuery();
            baseFile.close();
            redFile.close();
            yrDatabase->rollback();
            return false;
        }
    }


    // 提交事务
    if (!yrDatabase->commit()) {
        qWarning() << "事务提交失败:" << yrDatabase->lastError().text();
        yrDatabase->rollback();
        baseFile.close();
        redFile.close();
        return false;
    }

    baseFile.close();
    redFile.close();

    return true;
}

// 提取TestID的辅助函数
QString YRMainWindow::extractTestIdFromFileName(const QString &filePath) {
    QString fileName = QFileInfo(filePath).fileName();
    QStringList parts = fileName.split(QRegExp("[-_]"),  QString::SkipEmptyParts);
    return parts.isEmpty()  ? fileName : parts[0];
}

// 导入CSV文件的菜单槽函数
void YRMainWindow::on_action_csv_triggered() {
    QDialog dialog(this);
    dialog.setWindowTitle("导入CSV数据文件");

    // 创建布局
    QVBoxLayout *mainLayout = new QVBoxLayout(&dialog);
    QHBoxLayout *rowLayout1 = new QHBoxLayout();
    QHBoxLayout *rowLayout2 = new QHBoxLayout();

    // 创建标签和文本框
    QLabel *labelBefore = new QLabel("试验基本信息*.CSV文件路径:");
    QLineEdit *lineEditBefore = new QLineEdit();
    lineEditBefore->setReadOnly(true);

    QLabel *labelAfter = new QLabel("试验结果数据*_red.CSV文件路径:");
    QLineEdit *lineEditAfter = new QLineEdit();
    lineEditAfter->setReadOnly(true);

    // 创建按钮
    QPushButton *btnOpenBefore = new QPushButton("打开");
    QPushButton *btnOpenAfter = new QPushButton("打开");

    // 添加到布局
    rowLayout1->addWidget(labelBefore);
    rowLayout1->addWidget(lineEditBefore);
    rowLayout1->addWidget(btnOpenBefore);
    mainLayout->addLayout(rowLayout1);

    rowLayout2->addWidget(labelAfter);
    rowLayout2->addWidget(lineEditAfter);
    rowLayout2->addWidget(btnOpenAfter);
    mainLayout->addLayout(rowLayout2);

    // 创建确认和取消按钮
    QHBoxLayout *buttonLayout = new QHBoxLayout();
    QPushButton *btnConfirm = new QPushButton("确认");
    QPushButton *btnCancel = new QPushButton("取消");
    buttonLayout->addWidget(btnConfirm);
    buttonLayout->addWidget(btnCancel);
    mainLayout->addLayout(buttonLayout);

    // 连接信号和槽
    connect(btnOpenBefore, &QPushButton::clicked, [&]() {
        static bool flag = true;
        QString filePath;
        if (flag) {
            filePath = QFileDialog::getOpenFileName(this, "选择试验基本信息*.CSV文件", QStandardPaths::writableLocation(QStandardPaths::DesktopLocation), "CSV Files (*.csv)");
            flag = false;
        } else {
            filePath = QFileDialog::getOpenFileName(this, "选择试验基本信息*.CSV文件", "", "CSV Files (*.csv)");
        }
        lineEditBefore->setText(filePath);

//        static bool flag = true;
//        QStringList filePaths;
//        if (flag) {
//            filePaths = QFileDialog::getOpenFileNames(this,
//                                                      "选择试验基本信息和试验结果数据CSV文件",
//                                                      QStandardPaths::writableLocation(QStandardPaths::DesktopLocation),
//                                                      "CSV Files (*.csv)");
//            flag = false;
//        } else {
//            filePaths = QFileDialog::getOpenFileNames(this,
//                                                      "选择试验基本信息和试验结果数据CSV文件",
//                                                      "",
//                                                      "CSV Files (*.csv)");
//        }

//        if (filePaths.size() != 2) {
//            QMessageBox::warning(this, "错误", "请一次选择两个CSV文件（基本信息和结果数据）！");
//            return;
//        }

//        // 根据文件名自动判断哪个是基本信息，哪个是结果数据
//        QFileInfo file1(filePaths.at(0));
//        QFileInfo file2(filePaths.at(1));

//        if (file1.fileName().contains("_red", Qt::CaseInsensitive)) {
//            lineEditAfter->setText(filePaths.at(0)); // _red 文件
//            lineEditBefore->setText(filePaths.at(1)); // 基本信息文件
//        } else if (file2.fileName().contains("_red", Qt::CaseInsensitive)) {
//            lineEditAfter->setText(filePaths.at(1)); // _red 文件
//            lineEditBefore->setText(filePaths.at(0)); // 基本信息文件
//        } else {
//            QMessageBox::warning(this, "错误", "两个文件中必须包含一个以 '_red' 结尾的结果数据文件！");
//            return;
//        }
    });

    connect(btnOpenAfter, &QPushButton::clicked, [&]() {
        QString filePath = QFileDialog::getOpenFileName(this, "选择试验结果数据*_red.CSV文件", "", "CSV Files (*.csv)");
        QFileInfo file(filePath);
        //lineEditAfter->setText(file.fileName());
        lineEditAfter->setText(filePath);
    });

    connect(btnConfirm, &QPushButton::clicked, [&]() {
        QString pathBefore = lineEditBefore->text();
        QString pathAfter = lineEditAfter->text();

        // 检查文件名是否只有"_red"的差别
        QFileInfo fileInfoBefore(pathBefore);
        QFileInfo fileInfoAfter(pathAfter);
        QString baseNameBefore = fileInfoBefore.baseName();
        QString baseNameAfter = fileInfoAfter.baseName();

        if (baseNameBefore == baseNameAfter) {
            QMessageBox::warning(this, "错误", "CSV文件一致，请重新选择！");
            return;
        }

        if (!baseNameAfter.contains("red")) {
            QMessageBox::warning(this, "错误", "请检查*.csv与*_red.csv的文件路径");
            return;
        }

        if (baseNameBefore != baseNameAfter.left(baseNameBefore.length())) {
            QMessageBox::warning(this, "错误", "CSV文件非同一次试验");
            return;
        }

        QStringList testIDs;
        QSqlQuery query(yrDatabase);
        if (!query.exec("SELECT DISTINCT TestID FROM TestInfo ORDER BY TestID")) {
            qWarning() << "SELECT DISTINCT TestID FROM TestInfo ORDER BY TestID" << " 查询失败: " << query.lastError().text();
            return ;
        }

        while (query.next()) {
            testIDs << query.value(0).toString();
        }

        QString tempId = extractTestIdFromFileName(pathBefore);

        if (testIDs.contains(tempId)) {
            QString forwordId = nowTestID;
            nowTestID = tempId;
            QMessageBox::about(this, "提示", QString("试验ID为【%1】的数据已存在，下一步可选移除旧数据并导入新数据！").arg(tempId));
            bool imoportStatus = deleteTestIDFromDB();
            if (imoportStatus) {
                QMessageBox::about(this, "提示", QString("试验ID为【%1】的所有历史数据已删除，将执行导入步骤！").arg(tempId));
            } else {
                QMessageBox::warning(this, "警告", "用户取消删除操作，请重试导入功能！");
                return ;
            }
            nowTestID = forwordId;
        }


        // 执行导入操作
        bool success = importCSVToDatabase(pathBefore, &yrDatabase, false) &&
                       importCSVToDatabase(pathAfter, &yrDatabase, true);

        if (success) {
            // Add this line to import detailed data
            bool detailSuccess = importDetailedData(pathBefore, pathAfter, &yrDatabase);

            if (detailSuccess) {
                // 创建一个自定义的 QMessageBox
                QMessageBox msgBox;
                msgBox.setWindowTitle("成功");
                msgBox.setText("两类CSV数据导入成功");
                msgBox.setIcon(QMessageBox::Information);

                // 添加“继续导入”按钮
                QPushButton *btnContinue = msgBox.addButton("继续导入", QMessageBox::AcceptRole);
                // 添加“退出”按钮
                QPushButton *btnExit = msgBox.addButton("退出", QMessageBox::RejectRole);

                // 显示消息框并等待用户响应
                msgBox.exec();

                // 根据用户点击的按钮进行相应操作
                if (msgBox.clickedButton() == btnContinue) {
                    // 用户选择“继续导入”，可以重新打开文件选择对话框或者执行其他操作
                    // 例如，可以再次调用 on_action_csv_triggered() 函数
                    dialog.close();
                    on_action_csv_triggered();
                } else if (msgBox.clickedButton() == btnExit) {
                    // 用户选择“退出”，关闭对话框
                    dialog.close();
                }
            } else {
                QMessageBox::warning(this, "部分成功", "基本信息数据导入成功，但试验结果数据导入失败");
            }
        } else {
            QMessageBox::critical(this, "失败", "CSV数据导入失败");
        }

        loadTestIDsToTreeWidget("SELECT DISTINCT TestID FROM TestInfo ORDER BY TestID");
        update_count();

        dialog.close();
    });

    connect(btnCancel, &QPushButton::clicked, [&]() {
        dialog.close();
    });

    dialog.exec();
}

// 填充试验的TestID到TreeWidget
void YRMainWindow::loadTestIDsToTreeWidget(const QString &queryString, bool showQueryTime) {
    // 清空treeWidget
    ui->treeWidget->clear();

    // 确保不会自动生成默认项
    ui->treeWidget->setColumnCount(1);  // 设置需要的列数
    ui->treeWidget->header()->hide();   // 如果需要隐藏标题

    // 1. 从数据库获取TestID
    QStringList testIDs;
    QSqlQuery query(yrDatabase);

    // 获取当前时间戳（毫秒级精度）
    auto getCurrentTimestamp = []() -> QString {
        return QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss.zzz");
    };

    // 日志文件路径
    QString logFilePath = QDir(QCoreApplication::applicationDirPath()).filePath("mysql_query.log");

    // 记录查询开始时间
    QString startTime = getCurrentTimestamp();
    QElapsedTimer timer;
    timer.start();

    if (!query.exec(queryString)) {
        QString endTime = getCurrentTimestamp();
        qWarning() << queryString << " 查询失败: " << query.lastError().text();

        // 记录失败日志
        QFile logFile(logFilePath);
        if (logFile.open(QIODevice::Append | QIODevice::Text)) {
            QTextStream out(&logFile);
            out << QString("[%1] [ERROR] Query failed - %2\n").arg(endTime).arg(query.lastError().text());
            out << QString("  SQL: %1\n").arg(queryString);
            out << QString("  Start: %1, End: %2, Duration: %3 ms\n\n")
                   .arg(startTime).arg(endTime).arg(timer.elapsed());
            logFile.close();
        }
        return;
    }

    // 记录查询结束时间
    QString endTime = getCurrentTimestamp();
    qint64 elapsed = timer.elapsed();

    // 记录成功日志
    QFile logFile(logFilePath);
    if (logFile.open(QIODevice::Append | QIODevice::Text)) {
        QTextStream out(&logFile);
        out << QString("[%1] [INFO] Query executed successfully\n").arg(endTime);
        out << QString("  SQL: %1\n").arg(queryString);
        out << QString("  Start: %1, End: %2, Duration: %3 ms\n").arg(startTime).arg(endTime).arg(elapsed);
        out << QString("  Returned %1 rows\n\n").arg(query.size());
        logFile.close();
    }

    while (query.next()) {
        testIDs << query.value(0).toString();
    }

    qDebug() << testIDs;

    // 如果查询时间提示开启，则显示 QMessageBox
    if (showQueryTime) {
        QTimer::singleShot(0, this, [this, elapsed]() {
            QMessageBox::information(
                this,
                "查询耗时",
                QString("数据库查询耗时: %1 毫秒").arg(elapsed)
            );
        });
    }

    if (testIDs.isEmpty()) {
        QMessageBox::warning(
                    this,
                    "警告", // 对话框标题
                    QString("待查字段无匹配")  // 提示信息
                    );
        return;
    }

    // 2. 分类和排序TestID
    std::unordered_map<QString, QStringList> categorizedIDs; // 分类存储
    QStringList categoryOrder; // 保持分类顺序

    // 使用正则表达式匹配字母前缀和数字部分
    QRegExp regExp("^([A-Za-z]+)(\\d+)$");

    for (const QString &id : testIDs) {
        if (regExp.indexIn(id) != -1) {
            QString prefix = regExp.cap(1); // 字母部分
            QString number = regExp.cap(2); // 数字部分

            if (categorizedIDs.find(prefix) == categorizedIDs.end()) {
                categorizedIDs[prefix] = QStringList();
                categoryOrder.append(prefix); // 按插入顺序记录前缀
            }
            categorizedIDs[prefix].append(id);
        }
    }

    // 对每个分类中的 ID 按数字升序排序
    for (auto &pair : categorizedIDs) {
        QStringList &ids = pair.second;

        std::sort(ids.begin(), ids.end(), [](const QString &a, const QString &b) {
            static QRegularExpression rx("^(\\D+)(\\d+)$");

            auto matchA = rx.match(a);
            auto matchB = rx.match(b);

            if (matchA.hasMatch() && matchB.hasMatch()) {
                QString prefixA = matchA.captured(1);
                QString prefixB = matchB.captured(1);

                if (prefixA == prefixB) {
                    int numA = matchA.captured(2).toInt();
                    int numB = matchB.captured(2).toInt();
                    return numA < numB;
                }
                return prefixA < prefixB;
            }
            return a < b;
        });
    }

    //qDebug() << "排序：" << categorizedIDs;

    // 3. 填充TreeWidget
    for (const QString &prefix : categoryOrder) {
        // 创建父节点
        QTreeWidgetItem *parentItem = new QTreeWidgetItem(ui->treeWidget);
        parentItem->setText(0, prefix);
        parentItem->setData(0, Qt::UserRole, prefix); // 存储原始数据

        // 添加子节点
        for (const QString &childID : categorizedIDs[prefix]) {
            QTreeWidgetItem *childItem = new QTreeWidgetItem(parentItem);
            childItem->setText(0, childID);
            childItem->setData(0, Qt::UserRole, childID);
            //qDebug() << childID; // 输出子节点的 ID，确认顺序
        }

        ui->treeWidget->addTopLevelItem(parentItem);
    }

    // 展开所有节点
    ui->treeWidget->expandAll();

    // 禁用 QTreeWidget 的自动排序
    ui->treeWidget->setSortingEnabled(false);
}

// 单击TreeWidget中的子节点，触发该槽函数，展示对应TestID的试验基本信息表（Info与Results）到ui->tableView
void YRMainWindow::onTestIDClicked(QTreeWidgetItem *item, int column) {
    Q_UNUSED(column);

    if (!item->parent()) return;

    QString testID = item->text(0);
    qDebug() << "Loading data for TestID:" << testID;

    // 赋给全局变量
    nowTestID = testID;

    // 初始化模型
    if (!model) {
        model = new QStandardItemModel(this);
    }
    model->clear();
    model->setHorizontalHeaderLabels({"参数项", "数值", "参数项", "数值"});

    // 查询TestInfo表 - 使用参数化查询
    QSqlQuery infoQuery(yrDatabase);
    infoQuery.prepare("SELECT InfoName, InfoValue FROM TestInfo "
                     "WHERE TestID = ? ORDER BY id");  // 使用?占位符
    infoQuery.addBindValue(testID);

    if (!infoQuery.exec()) {
        qWarning() << "TestInfo查询失败:" << infoQuery.lastError().text();
        return;
    }

    // 查询TestResults表
    QSqlQuery resultQuery(yrDatabase);
    resultQuery.prepare("SELECT ResultName, ResultValue FROM TestResults "
                       "WHERE TestID = ? ORDER BY id");
    resultQuery.addBindValue(testID);

    if (!resultQuery.exec()) {
        qWarning() << "TestResults查询失败:" << resultQuery.lastError().text();
        return;
    }

    // 合并数据
    int maxRows = qMax(infoQuery.size(), resultQuery.size());
    for (int row = 0; row < maxRows; ++row) {
        model->insertRow(row);

        // 填充TestInfo数据
        if (infoQuery.seek(row)) {
            model->setItem(row, 0, new QStandardItem(engToChs(infoQuery.value(0).toString()).isEmpty() ? infoQuery.value(0).toString() : infoQuery.value(0).toString() + " | " + engToChs(infoQuery.value(0).toString())));
            model->setItem(row, 1, new QStandardItem(infoQuery.value(1).toString()));
        }

        // 填充TestResults数据
        if (resultQuery.seek(row)) {
            model->setItem(row, 2, new QStandardItem(engToChs(resultQuery.value(0).toString()).isEmpty() ? resultQuery.value(0).toString() : resultQuery.value(0).toString() + " | " + engToChs(resultQuery.value(0).toString())));
            model->setItem(row, 3, new QStandardItem(resultQuery.value(1).toString()));
        }
    }

    if (testID.startsWith("TD")) {
        model->takeColumn(3);
        model->takeColumn(2);
    }

    ui->tableView->setModel(model);
//    ui->tableView->resizeColumnsToContents();
//    ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);

    // 获取表格总宽度
    int tableWidth = ui->tableView->viewport()->width();
    // 计算列数
    int columnCount = model->columnCount();

    // 先按内容调整
    ui->tableView->resizeColumnsToContents();

    // 检查是否需要调整
    int totalWidth = 0;
    for (int i = 0; i < columnCount; ++i) {
        totalWidth += ui->tableView->columnWidth(i);
    }

    if (totalWidth < tableWidth) {
        // 如果内容宽度不足表格宽度，则按比例分配额外空间
        for (int i = 0; i < columnCount; ++i) {
            int currentWidth = ui->tableView->columnWidth(i);
            int newWidth = currentWidth + (tableWidth - totalWidth) * currentWidth / totalWidth;
            ui->tableView->setColumnWidth(i, newWidth);
        }
    } else {
        // 如果内容超出，则允许水平滚动
        ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
    }

    update_count();
}

// 单击TreeWidget中的子节点，触发该槽函数，展示对应TestID的试验详细数据表（Data）到ui->tableView_data
void YRMainWindow::onTestIDClickedToDataDisplay(QTreeWidgetItem *item, int column) {
    Q_UNUSED(column);

    if (!item->parent()) return;

    // 此处一定是切换了TreeWidget的节点，上次绘制的曲线清空
    lineNumber = 0;
    ui->customPlot->clearGraphs();
    ui->customPlot->replot();

    QString testID = item->text(0);
    qDebug() << "载入TestID对应的数据列:" << testID;

    // 初始化模型
    if (!data_model) {
        data_model = new QStandardItemModel(this);
    }
    data_model->clear();

    // 查询TestData表 - 使用参数化查询
    QSqlQuery dataQuery(yrDatabase);
    dataQuery.prepare("SELECT * FROM TestData "
                     "WHERE TestID = ? ORDER BY id");  // 使用?占位符
    dataQuery.addBindValue(testID);

    if (!dataQuery.exec()) {
        qWarning() << "TestData查询失败:" << dataQuery.lastError().text();
        return;
    }

    QSqlRecord record = dataQuery.record();
    for (int row = 0; row < dataQuery.size(); ++row) {
        if (dataQuery.seek(row)) {
            for (int i = 0; i < record.count(); ++i) {
                data_model->setItem(row, i, new QStandardItem(dataQuery.value(i).toString()));
            }
        }
    }

    // 获取列名
    for (int i = 0; i < record.count(); ++i) {
        QString englishName = record.fieldName(i);
        QString chineseName = csvColumnNameMap.value(englishName, englishName);
        data_model->setHeaderData(i, Qt::Horizontal, chineseName);
    }

    data_model->takeColumn(1);
    data_model->takeColumn(0);

    ui->tableView_data->setModel(data_model);
    ui->tableView_data->resizeColumnsToContents();
}

// 移除详细数据表中的空列
void YRMainWindow::removeEmptyColumns(bool keepFirstColumn = false) {
    if (!ui->tableView_data || !data_model || data_model->columnCount() == 0) return;

    // 保留首列（如ID列）
    int startCol = keepFirstColumn ? 1 : 0;

    for (int col = data_model->columnCount() - 1; col >= startCol; --col) {
        bool isEmpty = true;
        // 检查整个列的所有行是否为空
        for (int row = 0; row < data_model->rowCount(); ++row) {
            if (!data_model->data(data_model->index(row, col)).toString().trimmed().isEmpty()) {
                isEmpty = false;
                break;
            }
        }

        if (isEmpty) {
            data_model->removeColumn(col);
            qInfo() << "Removed empty column" << data_model->headerData(col, Qt::Horizontal).toString();
        }
    }
}

void YRMainWindow::onTestIDClickedToMainColumnDisplay(QTreeWidgetItem *item, int column) {
    Q_UNUSED(column);

    if (!item->parent()) return;

    QString testID = item->text(0);
    qDebug() << "载入TestID对应的数据列:" << testID;

    QStringList chineseColumns = {
        "引燃时间(s)",
        "熄灭时间(s)",
        "持续燃烧时间(s)",
        "最大热释放速率(kW/㎡)",
        "最大有效燃烧热(MJ/kg)",
        "最大质量损失速率(g/s)",
        "最大比消光面积(㎡/kg)",
        "最大CO生成率(kg/kg)",
        "最大CO2生成率(kg/kg)",
        "达到最大热释放速率的时间(s)",
        "达到最大有效燃烧热的时间(s)",
        "达到最大质量损失的时间(s)",
        "达到最大比消光面积的时间(s)",
        "达到最大CO生成率的时间(s)",
        "达到最大CO2生成率的时间(s)",
        "总热释放量(MJ/㎡)",
        "总耗氧量(g)",
        "质量损失(g)",
        "单位面积平均质量损失速率(g/(s·㎡))",
        "单位面积总产烟量(㎡/㎡)",
        "总产烟量(㎡)",
        "最大平均热释放速率(kW/㎡)"
    };

    QStringList englishColumns = {
        "Time to ignition (s)",
        "Time to flameout (s)",
        "Ignition to flameout(s)",
        "peak HRR (kW/㎡)",
        "peak EHC (MJ/kg)",
        "peak MLR (g/s)",
        "peak SEA (㎡/kg)",
        "peak COY (kg/kg)",
        "peak CO2Y (kg/kg)",
        "t peak HRR (s)",
        "t peak EHC (s)",
        "t peak MLR (s)",
        "t peak SEA (s)",
        "t peak COY (s)",
        "t peak CO2Y (s)",
        "Total heat release (MJ/㎡)",
        "Total oxygen consumed (g)",
        "Mass lost (g)",
        "Average specific MLR (g/(s·㎡))",
        "Total smoke release (㎡/㎡)",
        "Total smoke production (㎡)",
        "MARHE (kW/㎡)"
    };

    // 设置tableWidget的列数
    ui->tableWidget->setColumnCount(3);
    // 设置列标题
    QStringList headers = {"中文", "英文或缩写", "数值"};
    ui->tableWidget->setHorizontalHeaderLabels(headers);

    // 清除现有的行
    ui->tableWidget->clearContents();
    ui->tableWidget->setRowCount(0);

    QStringList dataSources;
    QString queryString;
    foreach (const QString& column, englishColumns) {
        QString line = cleanHeaderName(column).trimmed();
        if (line.startsWith("Time") || line.startsWith("Ignition")) {
            queryString = QString("(SELECT `InfoValue` FROM `TestInfo` WHERE `InfoName` LIKE '%%1%' AND `TestID` = '%2')").arg(line).arg(testID);
        } else {
            if (line.startsWith("Average") && testID.startsWith("TD")) line = "MLR per unit area";
            queryString = QString("(SELECT `ResultValue` FROM `TestResults` WHERE `ResultName` LIKE '%%1%' AND `TestID` = '%2')").arg(line).arg(testID);
        }

        QSqlQuery query(yrDatabase);
        if (!query.exec(queryString)) {
            qWarning() << queryString << " 查询失败: " << query.lastError().text();
            return ;
        }

        if (query.next()) {
            dataSources << query.value(0).toString();
            qDebug() << line << " : " << query.value(0).toString();
        } else {
            dataSources << "";
            qDebug() << line << " : " << "";
        }
    }

    qDebug() << "数值列：" <<dataSources;

    // 填充表格
    for (int i = 0; i < chineseColumns.size(); ++i) {
        ui->tableWidget->insertRow(i); // 插入新行
        ui->tableWidget->setItem(i, 0, new QTableWidgetItem(chineseColumns.at(i))); // 中文
        ui->tableWidget->setItem(i, 1, new QTableWidgetItem(englishColumns.at(i).startsWith("Average") && testID.startsWith("TD") ? "MLR per unit area (g/(s·㎡))" : englishColumns.at(i))); // 英文或缩写
        ui->tableWidget->setItem(i, 2, new QTableWidgetItem(dataSources.isEmpty() ? "" : dataSources.at(i)));
    }

    ui->tableWidget->resizeColumnsToContents();
    ui->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
}

// 辅助方法：清除旧模型
void YRMainWindow::clearTableView() {
    if (QAbstractItemModel *oldModel = ui->tableView->model()) {
        delete oldModel;
    }
}

// 读取两类csv的data，构造qmap，按time列为key，存储其他列数据time -> {column -> value}
void YRMainWindow::readCSVData(QFile& file, QMap<int, QMap<QString, QString>>& dataMap, bool isRedFile) {
    QTextStream stream(&file);
    QStringList headers;
    bool firstLine = true;
    bool secondLine = isRedFile; // red文件需要跳过第一行，第二行才是表头
    int lineCount = 0;
    const int DEBUG_MAX_LINES = 5; // 最多打印前5行数据

    qDebug() << "===== 开始读取" << (isRedFile ? "red" : "base") << "文件 =====";

    while (!stream.atEnd()) {
        lineCount++;
        QString line = stream.readLine();
        if (line.trimmed().isEmpty()) {
            qDebug() << "跳过空行，行号:" << lineCount;
            continue;
        }

        QStringList values = line.split(',');

        // 处理第一行
        if (firstLine) {
            if (isRedFile) {
                qDebug() << "red文件跳过第一行(Exported data)";
                firstLine = false;
                continue;
            } else {
                if (values.size() >= 3) {
                    headers = values.mid(2); // 移除前两列
                    qDebug() << "base文件表头:" << headers;
                }
                firstLine = false;
                continue;
            }
        }

        // red文件处理第二行（表头行）
        if (secondLine && isRedFile) {
            if (values.size() >= 3) {
                headers = values.mid(2); // 移除前两列
                qDebug() << "red文件表头:" << headers;
            }
            secondLine = false;
            continue;
        }

        // 确保有足够的数据列
        if (values.size() < 3) {
            qDebug() << "跳过列数不足的行，当前列数:" << values.size();
            continue;
        }

        // 提取时间点
        bool ok;
        int time = values[2].toInt(&ok);
        if (!ok) {
            qDebug() << "时间点解析失败，值:" << values[2];
            continue;
        }

        // 存储每列数据
        QMap<QString, QString> rowData;
        for (int i = 2; i < values.size(); ++i) {
            QString originalHeader = (i-2 < headers.size()) ? headers[i-2].trimmed() : QString("Column_%1").arg(i-1);
            QString cleanHeader = cleanHeaderName(originalHeader);

            // 根据文件类型选择映射表
            QString dbColumn;
            if(isRedFile) {
                dbColumn = Red_CSV_TO_DB_COLUMN_MAP.value(cleanHeader);
            }else {
                dbColumn = CSV_TO_DB_COLUMN_MAP.value(cleanHeader);
            }

            if (dbColumn.isEmpty()) {
                qDebug() << "警告: 未找到列名映射:" << cleanHeader << "原始列名:" << originalHeader;
                continue;
            }

            QString value = values[i].trimmed();
            rowData[dbColumn] = value;
        }

        dataMap[time] = rowData;

        // 打印前几行数据用于验证
        if (dataMap.size() <= DEBUG_MAX_LINES) {
            qDebug() << "成功读取时间点" << time << "的数据:";
            for (auto it = rowData.constBegin(); it != rowData.constEnd(); ++it) {
                qDebug() << "  " << it.key() << ":" << it.value();
            }
        }
    }

    qDebug() << "===== 文件读取完成 =====";
    qDebug() << "总计读取有效数据行数:" << dataMap.size();
    if (!dataMap.isEmpty()) {
        qDebug() << "时间点范围:" << dataMap.firstKey() << "->" << dataMap.lastKey();
    }
}

void YRMainWindow::readCSVDataDebug(QFile& file, QMap<int, QMap<QString, QVariant>>& dataMap, bool isRedFile) {
    QTextStream stream(&file);
    QStringList headers;
    bool firstLine = true;
    bool secondLine = isRedFile; // red文件需要跳过第一行，第二行才是表头
    int lineCount = 0;
    const int DEBUG_MAX_LINES = 5; // 最多打印前5行数据

    qDebug() << "===== 开始读取" << (isRedFile ? "red" : "base") << "文件 =====";

    while (!stream.atEnd()) {
        lineCount++;
        QString line = stream.readLine();
        if (line.trimmed().isEmpty()) {
            qDebug() << "跳过空行，行号:" << lineCount;
            continue;
        }

        QStringList values = line.split(',');
        qDebug() << "行" << lineCount << "原始数据:" << values;

        // 处理第一行
        if (firstLine) {
            if (isRedFile) {
                qDebug() << "red文件跳过第一行(Exported data)";
                firstLine = false;
                continue;
            } else {
                if (values.size() >= 3) {
                    headers = values.mid(2); // 移除前两列
                    qDebug() << "base文件表头:" << headers;
                }
                firstLine = false;
                continue;
            }
        }

        // red文件处理第二行（表头行）
        if (secondLine && isRedFile) {
            if (values.size() >= 3) {
                headers = values.mid(2); // 移除前两列
                qDebug() << "red文件表头:" << headers;
            }
            secondLine = false;
            continue;
        }

        // 确保有足够的数据列
        if (values.size() < 3) {
            qDebug() << "跳过列数不足的行，当前列数:" << values.size();
            continue;
        }

        // 提取时间点
        bool ok;
        int time = values[2].toInt(&ok);
        if (!ok) {
            qDebug() << "时间点解析失败，值:" << values[2];
            continue;
        }

        // 存储每列数据
        QMap<QString, QVariant> rowData;
        for (int i = 2; i < values.size(); ++i) {
            QString originalHeader = (i-2 < headers.size()) ? headers[i-2].trimmed() : QString("Column_%1").arg(i-1);
            QString cleanHeader = cleanHeaderName(originalHeader);
            qDebug() << "原始列名:" << originalHeader << "-> 处理后列名:" << cleanHeader;

            // 根据文件类型选择映射表
            QString dbColumn;
            if(isRedFile) {
                dbColumn = Red_CSV_TO_DB_COLUMN_MAP.value(cleanHeader);
            }else {
                dbColumn = CSV_TO_DB_COLUMN_MAP.value(cleanHeader);
            }

            if (dbColumn.isEmpty()) {
                qDebug() << "警告: 未找到列名映射:" << cleanHeader << "原始列名:" << originalHeader;
                continue;
            }

            QString value = values[i].trimmed();
            bool convertOk;
            double numValue = value.toDouble(&convertOk);
            rowData[dbColumn] = convertOk ? QVariant(numValue) : QVariant(value);

            qDebug() << "列映射:" << originalHeader << "->" << cleanHeader << "->" << dbColumn << "=" << value;
        }

        dataMap[time] = rowData;

        // 打印前几行数据用于验证
        if (dataMap.size() <= DEBUG_MAX_LINES) {
            qDebug() << "成功读取时间点" << time << "的数据:";
            for (auto it = rowData.constBegin(); it != rowData.constEnd(); ++it) {
                qDebug() << "  " << it.key() << ":" << it.value();
            }
        }
    }

    qDebug() << "===== 文件读取完成 =====";
    qDebug() << "总计读取有效数据行数:" << dataMap.size();
    if (!dataMap.isEmpty()) {
        qDebug() << "时间点范围:" << dataMap.firstKey() << "->" << dataMap.lastKey();
    }
}

// 清除字符串的单位
QString YRMainWindow::cleanHeaderName(const QString& originalHeader) {
    // 使用QString避免字符编码问题
    static const QChar brackets[] = { '(', QStringLiteral("（").at(0) };

    int bracketPos = -1;
    for (const QChar& bracket : brackets) {
        bracketPos = originalHeader.indexOf(bracket);
        if (bracketPos != -1) break;
    }

    return (bracketPos > 0) ? originalHeader.left(bracketPos).trimmed()
                            : originalHeader.trimmed();
}

QMap<QString, QString> YRMainWindow::getLineData(int timePoint, const QMap<int, QMap<QString, QString>>& fullData) {
    // 初始化所有可能的列，默认值为0.0
    QMap<QString, QString> result;

    // 列出所有可能的列名（根据你的实际需求调整）
    QStringList allColumns = {
        "Time", "O2", "DPT", "CO", "CO2", "PDM", "PDC", "Mass", "MethaneMFM",
        "APT", "HFM", "ColdTrapTC", "StackTC", "SmokeTC", "ConeHeaterTC",
        "SpareTC", "SootMFM", "AFM", "HRR", "EHC", "SEA", "MLR",
        "SpecificMLR", "COY", "CO2Y", "THR", "MFR", "k", "VStack",
        "VSmoke", "SPR", "RSR", "TSP", "TSR", "COP", "CO2P", "O2C",
        "TOC", "ARHE", "SpecimenMass", "Oxygen", "DPT_", "CO_", "CO2_",
        "PDM_", "PDC_", "APT_", "ColdTrapTC_", "StackTC_", "SmokeTC_",
        "ConeHeaterTC_", "SootMFM_", "SpareTC_", "HFM_"
    };

//    // 初始化所有列
//    foreach (const QString &column, allColumns) {
//        result[column] = 0.0;
//    }

    // 查找精确匹配的时间点
    auto it = fullData.find(timePoint);
    if (it != fullData.end()) {
        // 用实际数据覆盖默认值
        const QMap<QString, QString> &actualData = it.value();
        for (auto dataIt = actualData.constBegin(); dataIt != actualData.constEnd(); ++dataIt) {
            result[dataIt.key()] = dataIt.value();
        }
    }

    return result;
}

// 选中图例中的项，或者选中曲线时
void YRMainWindow::selectedShow() {
    for (int i=0; i < ui->customPlot->graphCount(); ++i) {
        QCPGraph *graph = ui->customPlot->graph(i);
        QCPPlottableLegendItem *item = ui->customPlot->legend->itemWithPlottable(graph);//图例中的每一个项

        //选中图例中的项，或者选中曲线时，两者都被选中
        if (item->selected() || graph->selected())
        {
            graph->setSelected(true);// 当图例项被选择时，选择图表全部的数据
            item->setSelected(true);
        }
    }
    ui->customPlot->replot();   //刷新图表
}

// 设置显示/隐藏菜单
void YRMainWindow::contextMenuRequest(QPoint pos) {
    QMenu *menu = new QMenu(this);
    menu->setAttribute(Qt::WA_DeleteOnClose);

    // 检查图例和图形是否存在
    bool hasLegend = ui->customPlot->legend && ui->customPlot->legend->visible();
    bool hasGraph = ui->customPlot->graphCount() > 0;

    // 仅当图例或图形有效时添加菜单项
    if (hasLegend || hasGraph) {
        double legendCounts = hasLegend ? ui->customPlot->legend->selectTest(pos, false) : -1;
        double graphCounts = hasGraph ? ui->customPlot->graph()->selectTest(pos, false) : -1;

        if (legendCounts >= 0 || graphCounts >= 0) {
            menu->addAction("显示", this, SLOT(showGraph()));
            menu->addAction("隐藏", this, SLOT(hideGraph()));
            menu->addAction("保存图像", this, SLOT(savePlot()));
            menu->addAction("清除曲线", this, SLOT(clearPlot()));
        }
    }

    menu->popup(ui->customPlot->mapToGlobal(pos));
}

// 被选中的曲线显示
void YRMainWindow::showGraph()
{
    for (int i=0; i<ui->customPlot->graphCount(); ++i)
    {
        QCPGraph *graphItem = ui->customPlot->graph(i);

        if (graphItem->selected())
        {
            graphItem->setVisible(true);
        }
    }
    ui->customPlot->replot();
}

// 被选中的曲线隐藏
void YRMainWindow::hideGraph()
{
    for (int i=0; i< ui->customPlot->graphCount(); ++i)
    {
        QCPGraph *graphItem = ui->customPlot->graph(i);

        if (graphItem->selected())
        {
            qDebug() << graphItem->name();
            graphItem->setVisible(false);
        }
    }
    ui->customPlot->replot();
}

// 保存当前Graph
void YRMainWindow::savePlot()
{
    QString fileName = QFileDialog::getSaveFileName(this, "保存图像", QStandardPaths::writableLocation(QStandardPaths::DesktopLocation), "PNG 图像 (*.png);;JPEG 图像 (*.jpg);;BMP 图像 (*.bmp)");
    if (!fileName.isEmpty()) {
        ui->customPlot->savePng(fileName);
        QMessageBox::information(this, "保存成功", "图像已成功保存！");
    }
}

// 清除所有曲线
void YRMainWindow::clearPlot()
{
    ui->customPlot->graph()->rescaleValueAxis(false, true);
    ui->customPlot->clearGraphs();
    ui->customPlot->replot();
    lineNumber = 0; // 重置曲线计数
    QMessageBox::information(this, "清除完成", "所有曲线已清除！");
}

// 绘制曲线函数
void YRMainWindow::draw(QString lineName, QVector<double> x, QVector<double> y, int lineNumber)
{
    if (ui->customPlot->graphCount() >= 10) {
        QMessageBox::warning(this, "警告", "曲线数量已达到上限！");
        return;
    }

    QPen pen;
    ui->customPlot->addGraph(); // 添加新曲线，而不是覆盖
    pen.setColor(QColor(qSin(lineNumber * 1 + 1.2) * 80 + 80,
                        qSin(lineNumber * 0.3 + 0) * 80 + 80,
                        qSin(lineNumber * 0.3 + 1.5) * 80 + 80));
    ui->customPlot->graph(ui->customPlot->graphCount() - 1)->setPen(pen); // 赋值给最新曲线
    ui->customPlot->graph(ui->customPlot->graphCount() - 1)->setName(lineName); // 图例名称
    ui->customPlot->graph(ui->customPlot->graphCount() - 1)->setLineStyle(QCPGraph::lsLine);
    ui->customPlot->graph(ui->customPlot->graphCount() - 1)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, 2));
    ui->customPlot->legend->setVisible(true);
    ui->customPlot->graph(ui->customPlot->graphCount() - 1)->setData(x, y);

    // 重新缩放坐标轴
    ui->customPlot->graph(ui->customPlot->graphCount() - 1)->rescaleAxes(true);
    ui->customPlot->graph(ui->customPlot->graphCount() - 1)->rescaleValueAxis(false, true);

    // 交互功能
    ui->customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom |
                                    QCP::iSelectAxes | QCP::iSelectLegend |
                                    QCP::iSelectPlottables | QCP::iMultiSelect);
    ui->customPlot->replot(); // 刷新图表
}

// 浮动窗口显示当前曲线点数据
void YRMainWindow::showPointToolTip(QMouseEvent *event)
{
    // 获取鼠标当前位置（相对于 QCustomPlot）
    double x = ui->customPlot->xAxis->pixelToCoord(event->pos().x());
    double y = ui->customPlot->yAxis->pixelToCoord(event->pos().y());

    // 初始化变量
    bool ok = false;
    double closestX = 0, closestY = 0;
    double minDistance = std::numeric_limits<double>::max();
    QString graphName;

    // 遍历所有图表（graph），查找最近的数据点
    for (int g = 0; g < ui->customPlot->graphCount(); ++g) {
        QCPGraph *graph = ui->customPlot->graph(g);
        if (!graph || graph->data()->isEmpty()) continue;

        // 遍历数据点
        for (auto it = graph->data()->constBegin(); it != graph->data()->constEnd(); ++it) {
            double key = it.key();
            double value = it.value().value;

            // 计算鼠标与数据点的距离
            double distance = qSqrt(qPow(x - key, 5) + qPow(y - value, 5));
            if (distance < 0.5 && distance < minDistance) { // 设置距离阈值
                closestX = key;
                closestY = value;
                minDistance = distance;
                graphName = graph->name();  // 获取当前图表名称（需提前设置）
                ok = true;
            }
        }
    }

    if (ok) {
        // 显示数据点信息，包括图表名称或编号
        QString text = QString("Graph: %1\nX: %2\nY: %3").arg(graphName).arg(closestX).arg(closestY);
        QToolTip::showText(event->globalPos(), text, ui->customPlot);
    } else {
        // 隐藏提示
        QToolTip::hideText();
    }
}

// 试验基本信息表，右键菜单
void YRMainWindow::showTableContextMenu(const QPoint &pos) {
    // 检查模型和选择模型是否有效
    if (!ui->tableView->model() || !ui->tableView->selectionModel()) {
        return;
    }

    QModelIndex selectedIndexes = ui->tableView->selectionModel()->currentIndex();
    if (!selectedIndexes.isValid()) {
        return; // 未选中
    }

    QMenu contextMenu(this);
    QAction *updateAction = new QAction("维护数据", &contextMenu);
    connect(updateAction, &QAction::triggered, this, &YRMainWindow::updateSelectedItem);
    contextMenu.addAction(updateAction);

    QAction *deleteAction = new QAction("删除该试验所有数据", &contextMenu);
    connect(deleteAction, &QAction::triggered, this, &YRMainWindow::deleteTestIDFromDB);
    contextMenu.addAction(deleteAction);

    if(login::userType == "User") {
        contextMenu.removeAction(updateAction);
        contextMenu.removeAction(deleteAction);
    }

    QAction *saveAllAction = new QAction("导出该试验所有数据", &contextMenu);
    connect(saveAllAction, &QAction::triggered,[=] () {
        QMessageBox::about(this, "提示", "请分别确认文件路径和文件名！");
        exportTableToCSV(ui->tableWidget);
        exportBaseTableViewToCSV();

        removeEmptyColumns(true);
        ui->blank_change->setText("显示空列");

        exportDetailTableViewToCSV();
    });
    contextMenu.addAction(saveAllAction);

    QAction *saveAction = new QAction("导出当前页", &contextMenu);
    connect(saveAction, &QAction::triggered, this, &YRMainWindow::exportBaseTableViewToCSV);
    contextMenu.addAction(saveAction);

    QAction *openAction = new QAction("打开文件", &contextMenu);
    QString selectName = model->data(model->index(ui->tableView->selectionModel()->currentIndex().row(),  0)).toString().split('|').first().trimmed();
    if (selectName.contains("Comments", Qt::CaseInsensitive) || selectName.contains("photo", Qt::CaseInsensitive) || selectName.contains("video", Qt::CaseInsensitive) || selectName.startsWith("Report file", Qt::CaseSensitive)) {
        connect(openAction, &QAction::triggered, [=]() {
            QString image_path = model->data(model->index(ui->tableView->selectionModel()->currentIndex().row(),  1)).toString();

            if(image_path.contains("\\")) image_path.replace("\\", "/");

            qDebug() << "需要打开的文件路径：" <<image_path;

            // 如果是相对路径，拼接 globalDirPath，转为绝对路径
            QString absolutePath = image_path.startsWith("..") ? globalDirPath + image_path.mid(QString("..").length()) : image_path;

            qDebug() << "绝对路径：" << absolutePath;

            QFileInfo fileInfo(absolutePath);
            if (!fileInfo.exists()) {
                qWarning() << "文件不存在:" << absolutePath;
                QMessageBox::warning(this, "警告", "文件不存在:" + absolutePath);
                return;
            }
            // 使用 QUrl::fromLocalFile 来创建一个 URL 对象
            QUrl fileUrl = QUrl::fromLocalFile(absolutePath);//应当放出来，fhkl表提供了假地址，代码暂时无效
            //QUrl fileUrl = QUrl::fromLocalFile("F:\\CUIT\\xfsjk\\试验V3\\实验台图片\\实验台.jpg");
            qDebug() << "需要打开的QUrl：" << fileUrl;
            // 使用 QDesktopServices::openUrl 来打开文件
            if (!QDesktopServices::openUrl(fileUrl)) {
                qWarning() << "文件URL打开失败:" << fileUrl << "请检查！";
            }
        });
        contextMenu.addAction(openAction);
    } else {
        contextMenu.removeAction(openAction);
    }

    contextMenu.exec(ui->tableView->viewport()->mapToGlobal(pos));
}

// 维护基本信息，update infovalue
void YRMainWindow::updateSelectedItem() {
    // 0. 检查全局变量有效性
    if (nowTestID.isEmpty())  {
        qWarning() << "nowTestID is empty!";
        return;
    }

    // 1. 获取当前选中项的数据
    QModelIndex selectedIndex = ui->tableView->selectionModel()->currentIndex();
    if (!selectedIndex.isValid())  {
        qWarning() << "No valid selection!";
        return;
    }

    QString InfoName = model->data(model->index(selectedIndex.row(),  0)).toString().split('|').first().trimmed();
    QString InfoValue = model->data(model->index(selectedIndex.row(),  1)).toString();

    // 2. 创建编辑对话框
    QDialog dialog(this);
    dialog.setWindowTitle("编辑试验信息");
    QFormLayout layout(&dialog);

    // 固定标签显示字段名（不可编辑）
    QLabel *nameLabel = new QLabel(InfoName, &dialog);
    layout.addRow(" 字段名称:", nameLabel);

    // 可编辑的值输入框
    QLineEdit *valueEdit = new QLineEdit(InfoValue, &dialog);
    layout.addRow(" 字段值:", valueEdit);

    QPushButton *filePathBtn;
    if (InfoName.contains("Comments") || InfoName.contains("comments") || InfoName.contains("photo") || InfoName.contains("video") || InfoName.startsWith("Report file")){
        filePathBtn = new QPushButton("打开", &dialog);
        layout.addRow(" 选择文件路径:", filePathBtn);
        QObject::connect(filePathBtn, &QPushButton::clicked, [=]() {
            QString filePath = QFileDialog::getOpenFileName(this, "选择文件", globalDirPath.isEmpty() ?  QStandardPaths::writableLocation(QStandardPaths::DesktopLocation) : globalDirPath, "All Files (*)");
            valueEdit->setText(filePath.startsWith(globalDirPath) ? QString("..") + filePath.mid(globalDirPath.length()) : filePath);
        });
    }

    // 确认/取消按钮
    QDialogButtonBox buttons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, &dialog);
    layout.addRow(&buttons);

    QObject::connect(&buttons, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
    QObject::connect(&buttons, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);

    // 3. 显示对话框并等待用户操作
    if (dialog.exec()  == QDialog::Accepted) {
        QString newValue = valueEdit->text().trimmed();

        // 4. 如果值被修改，更新数据库
        if (newValue != InfoValue) {
            QSqlQuery query(yrDatabase);
            query.prepare("UPDATE  TestInfo SET InfoValue = ? WHERE TestID = ? AND InfoName = ?");
            query.addBindValue(newValue);
            query.addBindValue(nowTestID);
            query.addBindValue(InfoName);

            if (!query.exec())  {
                qCritical() << "Update failed:" << query.lastError().text();
                QMessageBox::critical(this, "错误", "数据库更新失败！");
            } else {
                // 更新模型数据（立即刷新视图）
                model->setData(model->index(selectedIndex.row(),  1), newValue);
                qDebug() << "Updated:" << InfoName << "to" << newValue;
            }
        }
    }
}

// 移除指定TestID
bool YRMainWindow::deleteTestIDFromDB() {
    // 0. 检查全局变量有效性
    if (nowTestID.isEmpty())  {
        qWarning() << "nowTestID is empty!";
        QMessageBox::warning(this, "警告", "当前试验ID为空，无法删除！");
        return false;
    }

    // 1. 二次确认对话框
    QMessageBox::StandardButton confirm;
    confirm = QMessageBox::question(
                this,
                "确认删除",
                QString("确定要删除试验ID为【%1】现有的所有数据吗？\n此操作将永久删除TestInfo、TestResults、TestData三个表中的相关记录！").arg(nowTestID),
                QMessageBox::Yes | QMessageBox::No
                );

    if (confirm != QMessageBox::Yes) {
        return false; // 用户取消操作
    }

    // 2. 数据库操作（启用事务）
    bool success = true;
    yrDatabase.transaction();  // 开始事务

    // 删除三个表中的数据
    QStringList tables = {"TestInfo", "TestResults", "TestData"};
    foreach (const QString &table, tables) {
        QSqlQuery query(yrDatabase);
        query.prepare(QString("DELETE  FROM %1 WHERE TestID = ?").arg(table));
        query.addBindValue(nowTestID);

        if (!query.exec())  {
            qCritical() << "Delete from" << table << "failed:" << query.lastError().text();
            success = false;
            break; // 任一表删除失败则中断
        }
        qDebug() << "Deleted" << query.numRowsAffected()  << "rows from" << table;
    }

    // 3. 提交或回滚事务
    if (success) {
        yrDatabase.commit();
        QMessageBox::information(this, "成功", QString("试验ID【%1】原有的所有数据已删除！").arg(nowTestID));

        // 刷新模型数据（如有需要）
        model->clear();
        ui->tableView->setModel(model);

        data_model->clear();
        ui->tableView_data->setModel(data_model);

        ui->tableWidget->clearContents();
        ui->tableWidget->setRowCount(0);

        // 刷新TreeWidget
        loadTestIDsToTreeWidget("SELECT DISTINCT TestID FROM TestInfo ORDER BY TestID");
        update_count();

        return true;
    } else {
        yrDatabase.rollback();
        QMessageBox::critical(this, "错误", "删除操作失败，已回滚！");
        return false;
    }
}

// 试验详细数据表，右键菜单
void YRMainWindow::showTableDataContextMenu(const QPoint &pos) {
    // 检查模型和选择模型是否有效
    if (!ui->tableView_data->model() || !ui->tableView_data->selectionModel()) {
        return;
    }

    QModelIndexList selectedIndexes = ui->tableView_data->selectionModel()->selectedColumns();
    if (selectedIndexes.isEmpty()) {
        return; // 未选中列
    }

    QMenu contextMenu(this);
    QAction *plotAction = new QAction("绘图", &contextMenu);
    connect(plotAction, &QAction::triggered, this, &YRMainWindow::plotSelectedColumn);
    contextMenu.addAction(plotAction);

    QAction *saveAction = new QAction("导出当前页", &contextMenu);
    connect(saveAction, &QAction::triggered, this, &YRMainWindow::exportDetailTableViewToCSV);
    contextMenu.addAction(saveAction);

    contextMenu.exec(ui->tableView_data->viewport()->mapToGlobal(pos));
}

// 右键菜单->绘图，对应的槽函数
void YRMainWindow::plotSelectedColumn()
{
    QModelIndexList selectedIndexes = ui->tableView_data->selectionModel()->selectedColumns();
    if (selectedIndexes.isEmpty()) {
        qDebug() << "No column selected for plotting.";
        return;
    }

    if (lineNumber >= 10) {
        QMessageBox::warning(this, "警告", "曲线数量已达到上限！");
        return;
    }

    QAbstractItemModel *model = ui->tableView_data->model();
    if (!model) return;

    int timeColumn = 0; // 假设时间列是第 0 列
    QVector<double> timeData;
    for (int row = 0; row < model->rowCount(); ++row) {
        bool ok;
        double timeValue = model->index(row, timeColumn).data().toDouble(&ok);
        if (ok) {
            timeData.append(timeValue);
        }
    }

    if (timeData.isEmpty()) {
        qDebug() << "No valid time data for plotting.";
        return;
    }

    for (const QModelIndex &index : selectedIndexes) {
        int selectedColumn = index.column();
        QString columnName = model->headerData(selectedColumn, Qt::Horizontal).toString();
        QVector<double> selectedData;

        for (int row = 0; row < model->rowCount(); ++row) {
            bool ok;
            double columnValue = model->index(row, selectedColumn).data().toDouble(&ok);
            if (ok) {
                selectedData.append(columnValue);
            }
        }

        if (!selectedData.isEmpty()) {
            draw(columnName, timeData, selectedData, lineNumber); // 使用当前曲线编号
            lineNumber++; // 递增曲线编号
            if (lineNumber >= 10) {
                QMessageBox::warning(this, "警告", "曲线数量已达到上限！");
                return;
            }
        }
    }

    // 跳转到第3个标签页（索引从0开始）
    ui->tabWidget->setCurrentIndex(3);
    if (firstShow) {
        QMessageBox::warning(this, "提示", "使用“绘图”功能会自动跳转到该界面，当界面上有曲线时，鼠标指针靠近折线，可显示坐标点数据。右键唤出菜单，在弹出菜单中选择“显示”或“隐藏”或“保存图像”或“清除曲线”。“显示”可使隐藏的曲线显示。“隐藏”可使蓝色高亮曲线隐藏。“保存图像”可保存当前页的曲线图。“清除曲线”可清空当前页。");
        firstShow = false;
    }
}

// 导出基本信息数据
void YRMainWindow::exportBaseTableViewToCSV() {
    // 检查tableView是否有模型和数据
    QAbstractItemModel* model = ui->tableView->model();
    if (!model || model->rowCount() == 0) {
        QMessageBox::warning(nullptr, "警告", "表格为空，无法导出！");
        return;
    }

    // 获取用户指定的文件名和路径
    QString defaultFileName = nowTestID + "_试验基本信息.csv"; // 设置默认文件名
    QString filePath = QFileDialog::getSaveFileName(nullptr, "导出为CSV文件", QStandardPaths::writableLocation(QStandardPaths::DesktopLocation) + "/" + defaultFileName, "CSV文件 (.csv)");
    if (filePath.isEmpty()) {
        return; // 用户取消了保存
    }

    // 确保文件名以.csv结尾
    if (!filePath.endsWith(".csv", Qt::CaseInsensitive)) {
        filePath += ".csv";
    }

    // 打开文件
    QFile file(filePath);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        QMessageBox::critical(nullptr, "错误", "无法打开文件！");
        return;
    }

    QTextStream out(&file);

    out.setCodec("GBK"); // 使用 GBK 编码

    // 写入表头
    for (int column = 0; column < model->columnCount(); ++column) {
        out << "\"" << model->headerData(column, Qt::Horizontal).toString() << "\"";
        if (column < model->columnCount() - 1) out << ",";
    }
    out << "\n";

    // 写入数据
    for (int row = 0; row < model->rowCount(); ++row) {
        for (int column = 0; column < model->columnCount(); ++column) {
            QModelIndex index = model->index(row, column);
            out << "\"" << model->data(index).toString() << "\"";
            if (column < model->columnCount() - 1) out << ",";
        }
        out << "\n";
    }

    // 关闭文件
    file.close();

    QMessageBox::information(nullptr, "成功", "导出成功！");
}

// 导出详细数据
void YRMainWindow::exportDetailTableViewToCSV() {
    // 检查tableView是否有模型和数据
    QAbstractItemModel* model = ui->tableView_data->model();
    if (!model || model->rowCount() == 0) {
        QMessageBox::warning(nullptr, "警告", "表格为空，无法导出！");
        return;
    }

    // 获取用户指定的文件名和路径
    QString defaultFileName = nowTestID + "_试验详细信息.csv"; // 设置默认文件名
    QString filePath = QFileDialog::getSaveFileName(nullptr, "导出为CSV文件", QStandardPaths::writableLocation(QStandardPaths::DesktopLocation) + "/" + defaultFileName, "CSV文件 (.csv)");
    if (filePath.isEmpty()) {
        return; // 用户取消了保存
    }

    // 确保文件名以.csv结尾
    if (!filePath.endsWith(".csv", Qt::CaseInsensitive)) {
        filePath += ".csv";
    }

    // 打开文件
    QFile file(filePath);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        QMessageBox::critical(nullptr, "错误", "无法打开文件！");
        return;
    }

    QTextStream out(&file);

    out.setCodec("GBK"); // 使用 GBK 编码

    // 写入表头
    for (int column = 0; column < model->columnCount(); ++column) {
        out << "\"" << model->headerData(column, Qt::Horizontal).toString() << "\"";
        if (column < model->columnCount() - 1) out << ",";
    }
    out << "\n";

    // 写入数据
    for (int row = 0; row < model->rowCount(); ++row) {
        for (int column = 0; column < model->columnCount(); ++column) {
            QModelIndex index = model->index(row, column);
            out << "\"" << model->data(index).toString() << "\"";
            if (column < model->columnCount() - 1) out << ",";
        }
        out << "\n";
    }

    // 关闭文件
    file.close();

    QMessageBox::information(nullptr, "成功", "导出成功！");
}

// 导入CSV文件的菜单槽函数，适配东林三类csv
void YRMainWindow::on_action_three_csv_triggered()
{
    QDialog dialog(this);
    dialog.setWindowTitle("导入CSV数据文件");

    // 创建布局
    QVBoxLayout *mainLayout = new QVBoxLayout(&dialog);
    QHBoxLayout *rowLayout1 = new QHBoxLayout();
    QHBoxLayout *rowLayout2 = new QHBoxLayout();
    QHBoxLayout *rowLayout3 = new QHBoxLayout();

    // 创建标签和文本框
    QLabel *labelRes = new QLabel("*_res.CSV文件路径:");
    QLineEdit *lineEditRes = new QLineEdit();
    lineEditRes->setReadOnly(true);

    QLabel *labelBefore = new QLabel("*_base.CSV文件路径:");
    QLineEdit *lineEditBefore = new QLineEdit();
    lineEditBefore->setReadOnly(true);

    QLabel *labelAfter = new QLabel("*_data.CSV文件路径:");
    QLineEdit *lineEditAfter = new QLineEdit();
    lineEditAfter->setReadOnly(true);

    // 创建按钮
    QPushButton *btnOpenRes = new QPushButton("打开");
    QPushButton *btnOpenBefore = new QPushButton("打开");
    QPushButton *btnOpenAfter = new QPushButton("打开");

    // 添加到布局
    rowLayout1->addWidget(labelRes);
    rowLayout1->addWidget(lineEditRes);
    rowLayout1->addWidget(btnOpenRes);
    mainLayout->addLayout(rowLayout1);

    rowLayout2->addWidget(labelBefore);
    rowLayout2->addWidget(lineEditBefore);
    rowLayout2->addWidget(btnOpenBefore);
    mainLayout->addLayout(rowLayout2);

    rowLayout3->addWidget(labelAfter);
    rowLayout3->addWidget(lineEditAfter);
    rowLayout3->addWidget(btnOpenAfter);
    mainLayout->addLayout(rowLayout3);

    // 创建确认和取消按钮
    QHBoxLayout *buttonLayout = new QHBoxLayout();
    QPushButton *btnConfirm = new QPushButton("确认");
    QPushButton *btnCancel = new QPushButton("取消");
    buttonLayout->addWidget(btnConfirm);
    buttonLayout->addWidget(btnCancel);
    mainLayout->addLayout(buttonLayout);

    // 连接信号和槽
    connect(btnOpenRes, &QPushButton::clicked, [&]() {
        static bool flag = true;
        QString filePath;
        if (flag) {
            filePath = QFileDialog::getOpenFileName(this, "选择*_res.CSV文件", QStandardPaths::writableLocation(QStandardPaths::DesktopLocation), "CSV Files (*.csv)");
            flag = false;
        } else {
            filePath = QFileDialog::getOpenFileName(this, "选择*_res.CSV文件", "", "CSV Files (*.csv)");
        }
        QFileInfo file(filePath);
        //lineEditBefore->setText(file.fileName());
        lineEditRes->setText(filePath);
    });

    connect(btnOpenBefore, &QPushButton::clicked, [&]() {
        QString filePath = QFileDialog::getOpenFileName(this, "选择*_base.CSV文件", "", "CSV Files (*.csv)");
        QFileInfo file(filePath);
        //lineEditBefore->setText(file.fileName());
        lineEditBefore->setText(filePath);
    });

    connect(btnOpenAfter, &QPushButton::clicked, [&]() {
        QString filePath = QFileDialog::getOpenFileName(this, "选择*_data.CSV文件", "", "CSV Files (*.csv)");
        QFileInfo file(filePath);
        //lineEditAfter->setText(file.fileName());
        lineEditAfter->setText(filePath);
    });

    connect(btnConfirm, &QPushButton::clicked, [&]() {

        QString pathRes = lineEditRes->text();
        QString pathBefore = lineEditBefore->text();
        QString pathAfter = lineEditAfter->text();

        // 检查文件名是否只有"_red"的差别
        QFileInfo fileInfoRes(pathRes);
        QFileInfo fileInfoBefore(pathBefore);
        QFileInfo fileInfoAfter(pathAfter);
        QString baseNameRes = fileInfoRes.baseName();
        QString baseNameBefore = fileInfoBefore.baseName();
        QString baseNameAfter = fileInfoAfter.baseName();

        if (baseNameRes == baseNameBefore || baseNameRes == baseNameAfter || baseNameBefore == baseNameAfter) {
            QMessageBox::warning(this, "错误", "CSV文件一致，请重新选择！");
            return;
        }

        if (extractTestIdFromFileName(baseNameRes) != extractTestIdFromFileName(baseNameBefore) || extractTestIdFromFileName(baseNameRes) != extractTestIdFromFileName(baseNameAfter)
                || extractTestIdFromFileName(baseNameBefore) != extractTestIdFromFileName(baseNameAfter)) {
            QMessageBox::warning(this, "错误", "三类CSV文件非同一次试验");
            return;
        }

        if (!baseNameBefore.contains("base") || !baseNameAfter.contains("data")) {
            QMessageBox::warning(this, "错误", "请检查*_data.csv与*_base.csv的文件路径");
            return;
        }

        QStringList testIDs;
        QSqlQuery query(yrDatabase);
        if (!query.exec("SELECT DISTINCT TestID FROM TestInfo ORDER BY TestID")) {
            qWarning() << "SELECT DISTINCT TestID FROM TestInfo ORDER BY TestID" << " 查询失败: " << query.lastError().text();
            return ;
        }

        while (query.next()) {
            testIDs << query.value(0).toString();
        }

        QString tempId = extractTestIdFromFileName(pathBefore);

        if (testIDs.contains(tempId)) {
            QString forwordId = nowTestID;
            nowTestID = tempId;
            QMessageBox::about(this, "提示", QString("试验ID为【%1】的数据已存在，下一步可选移除旧数据并导入新数据！").arg(tempId));
            bool imoportStatus = deleteTestIDFromDB();
            if (imoportStatus) {
                QMessageBox::about(this, "提示", QString("试验ID为【%1】的所有历史数据已删除，将执行导入步骤！").arg(tempId));
            } else {
                QMessageBox::warning(this, "警告", "用户取消删除操作，请重试导入功能！");
                return ;
            }
            nowTestID = forwordId;
        }

        // 执行导入操作
        bool success = importCSVToDatabase(pathRes, &yrDatabase, false) &&
                       importCSVToDatabase(pathRes, &yrDatabase, true);

        if (success) {
            // Add this line to import detailed data
            bool detailSuccess = importDonglinDetailedData(pathBefore, pathAfter, &yrDatabase);

            if (detailSuccess) {
                // 创建一个自定义的 QMessageBox
                QMessageBox msgBox;
                msgBox.setWindowTitle("成功");
                msgBox.setText("三类CSV数据导入成功");
                msgBox.setIcon(QMessageBox::Information);

                // 添加“继续导入”按钮
                QPushButton *btnContinue = msgBox.addButton("继续导入", QMessageBox::AcceptRole);
                // 添加“退出”按钮
                QPushButton *btnExit = msgBox.addButton("退出", QMessageBox::RejectRole);

                // 显示消息框并等待用户响应
                msgBox.exec();

                // 根据用户点击的按钮进行相应操作
                if (msgBox.clickedButton() == btnContinue) {
                    // 用户选择“继续导入”，可以重新打开文件选择对话框或者执行其他操作
                    // 例如，可以再次调用 on_action_three_csv_triggered() 函数
                    dialog.close();
                    on_action_three_csv_triggered();
                } else if (msgBox.clickedButton() == btnExit) {
                    // 用户选择“退出”，关闭对话框
                    dialog.close();
                }
            } else {
                QMessageBox::warning(this, "部分成功", "基本信息数据（res）导入成功，但试验结果数据（base+data）导入失败");
            }
        } else {
            QMessageBox::critical(this, "失败", "CSV数据导入失败");
        }

        loadTestIDsToTreeWidget("SELECT DISTINCT TestID FROM TestInfo ORDER BY TestID");
        update_count();

        dialog.close();
    });

    connect(btnCancel, &QPushButton::clicked, [&]() {
        dialog.close();
    });

    dialog.exec();
}

// 导入东林数据列
bool YRMainWindow::importDonglinDetailedData(const QString &baseFilePath, const QString &dataFilePath, QSqlDatabase *yrDatabase) {
    // 检查数据库连接
    if (!yrDatabase || !yrDatabase->isOpen()) {
        qWarning() << "数据库未连接或无效";
        return false;
    }

    QFile baseFile(baseFilePath);
    QFile dataFile(dataFilePath);

    if (!baseFile.open(QIODevice::ReadOnly | QIODevice::Text) ||
            !dataFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qWarning() << "无法打开文件";
        return false;
    }

    // 开启事务
    if (!yrDatabase->transaction()) {
        qWarning() << "事务启动失败:" << yrDatabase->lastError().text();
        baseFile.close();
        dataFile.close();
        return false;
    }

    QSqlQuery query(*yrDatabase);

    // 提取TestID
    QString testId = extractTestIdFromFileName(baseFilePath);
    if (testId.isEmpty()) {
        qWarning() << "无效的TestID";
        return false;
    }

    QString insertSQL = "INSERT INTO TestData (TestID, Time, O2, DPT, CO, CO2, PDM, PDC, "
                        "Mass, MethaneMFM, APT, HFM, ColdTrapTC, StackTC, SmokeTC, ConeHeaterTC, SpareTC, "
                        "SootMFM, AFM, HRR, EHC, SEA, MLR, SpecificMLR, COY, CO2Y, THR, MFR, k, VStack, VSmoke, "
                        "SPR, RSR, TSP, TSR, COP, CO2P, O2C, TOC, ARHE, SpecimenMass, Oxygen, DPT_, CO_, CO2_, PDM_, "
                        "PDC_, APT_, ColdTrapTC_, StackTC_, SmokeTC_, ConeHeaterTC_, SootMFM_, SpareTC_, HFM_) VALUES (?, ?, ?, ?, ?, "
                        "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, "
                        "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";

    query.prepare(insertSQL);

    // 构造5秒间隔的时间点序列，提取dataFile的Time。即timePoints为Time列的内容
    QList<int> timePoints;
    {
        QFile dataFileT(dataFilePath);
        if (!dataFileT.open(QIODevice::ReadOnly | QIODevice::Text)) {
            qWarning() << "无法打开*_data.CSV文件";
            return false;
        }

        QTextStream dataStream(&dataFileT);
        bool isFirstLine = true;
        int timeColumnIndex = -1;

        while (!dataStream.atEnd()) {
            QString line = dataStream.readLine();
            if (line.trimmed().isEmpty()) continue;

            // Split by comma (CSV format)
            QStringList values = line.split(',');

            // Process the header line (second line)
            if (isFirstLine) {
                isFirstLine = false;
                timeColumnIndex = values.indexOf("Time(s)");
                if (timeColumnIndex == -1) {
                    qWarning() << "未找到Time (s)列";
                    dataFileT.close();
                    return false;
                }
                continue;
            }

            // Process data lines
            if (values.size() > timeColumnIndex) {
                bool ok;
                // Remove any quotes or whitespace from the time value
                QString timeStr = values[timeColumnIndex].trimmed().remove('"');
                int time = timeStr.toInt(&ok);
                if (ok) {
                    timePoints.append(time);
                } else {
                    qWarning() << "无效的时间值:" << timeStr;
                }
            }
        }
        dataFileT.close();

        qDebug() << "提取到的时间点数量:" << timePoints.size();
        if (!timePoints.isEmpty()) {
            qDebug() << "时间点示例:" << timePoints.first() << "..." << timePoints.last();
        }
    }


    // 读取两个CSV文件的数据
    QMap<int, QMap<QString, QString>> allData;  // time -> {column -> value}

    // 读取各列数据
    // 读取baseFile和dataFile（保留第一行作为表头）
    readDonglinCSVData(baseFile, dataFile, allData);

    // 构建QMap，存储*_base.CSV与*_data.CSV中符合时间间隔为5s的数据行
    QMap<QString, QString> rowData;// {column -> value}

    // 插入每个5秒间隔的数据点
    for (int timePoint : timePoints) {
        query.bindValue(0, testId);
        query.bindValue(1, timePoint);

        int paramIndex = 2;  // 从第3个参数开始绑定

        rowData = getLineData(timePoint, allData);  // 获取指定 timePoint 当行的所有列数据

        for (auto it = rowData.begin(); it != rowData.end(); ++it) {
            qDebug() << "rowData映射表:" << it.key() << " -> " << it.value();
        }

        // 绑定 *_base.CSV 和 *_data.CSV 数据的各列，没有的数据绑定 0.0
        QStringList columns = {
            "O2", "DPT", "CO", "CO2", "PDM", "PDC", "Mass", "MethaneMFM", "APT",
            "HFM", "ColdTrapTC", "StackTC", "SmokeTC", "ConeHeaterTC", "SpareTC",
            "SootMFM", "AFM", "HRR", "EHC", "SEA", "MLR", "SpecificMLR", "COY", "CO2Y", "THR", "MFR", "k",
            "VStack", "VSmoke", "SPR", "RSR", "TSP", "TSR", "COP", "CO2P", "O2C", "TOC",
            "ARHE", "SpecimenMass", "Oxygen", "DPT_", "CO_", "CO2_", "PDM_", "PDC_",
            "APT_", "ColdTrapTC_", "StackTC_", "SmokeTC_", "ConeHeaterTC_",
            "SootMFM_", "SpareTC_", "HFM_"
        };

        for (const QString &col : columns) {
            query.bindValue(paramIndex++, rowData.value(col, ""));
        }


        qDebug() << "paraIndex: " << paramIndex;

        QString sql = query.lastQuery();
        QMap<QString, QVariant> boundValues = query.boundValues();
        QRegularExpression regex("\\?");
        int pos = 0;

        for (auto it = boundValues.begin(); it != boundValues.end(); ++it) {
            QString valueStr;
            if (it.value().type() == QVariant::String) {
                valueStr = "'" + it.value().toString().replace("'", "''") + "'";
            } else if (it.value().isNull()) {
                valueStr = "NULL";
            } else {
                valueStr = it.value().toString();
            }

            pos = sql.indexOf('?', pos);
            if (pos != -1) {
                sql.replace(pos, 1, valueStr);
                pos += valueStr.length();
            } else {
                break;
            }
        }

        qDebug() << "即将执行的SQL:" << sql;
        qDebug() << "绑定的参数数量:" << query.boundValues().size();

        if (!query.exec()) {
            qWarning() << "插入失败:" << query.lastError().text()
                       << "\nSQL:" << query.lastQuery();
            baseFile.close();
            dataFile.close();
            yrDatabase->rollback();
            return false;
        }
    }


    // 提交事务
    if (!yrDatabase->commit()) {
        qWarning() << "事务提交失败:" << yrDatabase->lastError().text();
        yrDatabase->rollback();
        baseFile.close();
        dataFile.close();
        return false;
    }

    baseFile.close();
    dataFile.close();

    return true;
}

// 读取*_base.csv与*_data.csv的数据列，构造qmap，按time列为key，存储所有列数据time -> {column -> value}
void YRMainWindow::readDonglinCSVData(QFile& baseFile, QFile& dataFile, QMap<int, QMap<QString, QString>>& dataMap) {
    QTextStream baseStream(&baseFile);
    QTextStream dataStream(&dataFile);
    QStringList baseHeaders, dataHeaders;
    QMap<int, QMap<QString, QString>> baseData;
    QMap<int, QMap<QString, QString>> dataData;
    bool firstBaseLine = true, firstDataLine = true;

    qDebug() << "===== 开始读取 CSV 文件 =====";

    // 读取 base.csv
    while (!baseStream.atEnd()) {
        QString line = baseStream.readLine();
        if (line.trimmed().isEmpty()) continue;
        QStringList values = line.split(',');

        if (firstBaseLine) {
            baseHeaders = values;
            firstBaseLine = false;
            continue;
        }

        bool ok;
        int time = values[0].toInt(&ok);
        if (!ok) continue;

        QMap<QString, QString> rowData;
        for (int i = 0; i < values.size(); ++i) {
            QString cleanHeader = cleanHeaderName(baseHeaders.value(i, QString("Column_%1").arg(i+1)));
            QString dbColumn = BASE_CSV_TO_DB_COLUMN_MAP.value(cleanHeader.trimmed());
            if (!dbColumn.isEmpty()) rowData[dbColumn] = values[i].trimmed();
        }
        baseData[time] = rowData;
    }

    // 读取 data.csv
    while (!dataStream.atEnd()) {
        QString line = dataStream.readLine();
        if (line.trimmed().isEmpty()) continue;
        QStringList values = line.split(',');

        if (firstDataLine) {
            dataHeaders = values;
            firstDataLine = false;
            continue;
        }

        bool ok;
        int time = values[0].toInt(&ok);
        if (!ok) continue;

        QMap<QString, QString> rowData;
        for (int i = 0; i < values.size(); ++i) {
            QString cleanHeader = cleanHeaderName(dataHeaders.value(i, QString("Column_%1").arg(i+1)));
            QString dbColumn = DATA_CSV_TO_DB_COLUMN_MAP.value(cleanHeader.trimmed());
            if (!dbColumn.isEmpty()) rowData[dbColumn] = values[i].trimmed();
        }
        dataData[time] = rowData;
    }

    // 合并数据，以 dataData 的 key 为主
    for (auto it = dataData.begin(); it != dataData.end(); ++it) {
        int time = it.key();
        QMap<QString, QString> mergedRow = it.value();

        // 如果 baseData 中存在相同的 key，则合并数据
        if (baseData.contains(time)) {
            for (auto baseIt = baseData[time].begin(); baseIt != baseData[time].end(); ++baseIt) {
                mergedRow[baseIt.key()] = baseIt.value();
            }
        }

        dataMap[time] = mergedRow;
    }

    qDebug() << "===== CSV 文件合并完成 =====";
    qDebug() << "合并后的数据点数量:" << dataMap.size();
}

// 查询对话框槽函数
void YRMainWindow::on_pushButton_search_clicked()
{
    // 如果对话框已存在且未被删除，则激活它
    if (dialog && dialog->isVisible()) {
        dialog->activateWindow();
        return;
    }

    // 确保旧对话框被正确清理
    if (dialog) {
        dialog->deleteLater();
        dialog = nullptr;
    }

    dialog = new SearchDialog(yrDatabase, this);
    dialog->setAttribute(Qt::WA_DeleteOnClose);
    connect(dialog, &QObject::destroyed, this, [this]() {
        dialog = nullptr; // 对话框被销毁时重置指针
    });

    dialog->setWindowIcon(QIcon(":/images/fire.png"));
    dialog->setWindowTitle("试验查询对话框");
    dialog->show();

    //qDebug() << children(); // 主窗口中应包含对话框指针
}

// 显示所有试验记录到TreeWidget，对应刷新按钮
void YRMainWindow::on_pushButton_clicked()
{
    // 填充所有TestID到treeWidget
    loadTestIDsToTreeWidget("SELECT DISTINCT TestID FROM TestInfo ORDER BY TestID");
    update_count();
    ui->statusbar->showMessage("试验编号： ", 0);

    // 清空曲线
    if (ui->customPlot->graphCount() > 0) {
        ui->customPlot->graph()->rescaleValueAxis(false, true);
    }
    ui->customPlot->clearGraphs();
    ui->customPlot->replot();
    lineNumber = 0; // 重置曲线计数

    // 清空展示界面
    if (!model) {
        model = new QStandardItemModel(this);
    }
    if (!data_model) {
        data_model = new QStandardItemModel(this);
    }
    model->clear();
    data_model->clear();
    ui->tableView->setModel(model);
    ui->tableView_data->setModel(data_model);

    ui->tableWidget->clearContents();
    ui->tableWidget->setRowCount(0);  // 删除所有行
    ui->tableWidget->setColumnCount(0);  // 删除所有列

}

// 导出重要参数值
void YRMainWindow::exportTableToCSV(QTableWidget *tableWidget)
{
    // 检查tableWidget是否为空
    if (tableWidget->rowCount() == 0)
    {
        QMessageBox::warning(nullptr, "警告", "表格为空，无法导出！");
        return;
    }

    // 获取用户指定的文件名和路径
    QString defaultFileName = nowTestID + "_重要特征参数.csv"; // 设置默认文件名
    QString filePath = QFileDialog::getSaveFileName(nullptr, "导出为CSV文件", QStandardPaths::writableLocation(QStandardPaths::DesktopLocation) + "/" + defaultFileName, "CSV文件 (.csv)");
    if (filePath.isEmpty())
    {
        return; // 用户取消了保存
    }

    // 确保文件名以.csv结尾
    if (!filePath.endsWith(".csv", Qt::CaseInsensitive)) {
        filePath += ".csv";
    }

    // 打开文件
    QFile file(filePath);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        QMessageBox::critical(nullptr, "错误", "无法创建文件！");
        return;
    }

    QTextStream out(&file);

    out.setCodec("GBK"); // 使用 GBK 编码

    // 写入表头
    for (int column = 0; column < tableWidget->columnCount(); ++column)
    {
        QTableWidgetItem *headerItem = tableWidget->horizontalHeaderItem(column);
        out << "\"" << (headerItem ? headerItem->text() : "") << "\"";
        if (column < tableWidget->columnCount() - 1) out << ",";
    }
    out << "\n";

    // 写入数据行
    for (int row = 0; row < tableWidget->rowCount(); ++row)
    {
        for (int column = 0; column < tableWidget->columnCount(); ++column)
        {
            QTableWidgetItem *item = tableWidget->item(row, column);
            out << "\"" << (item ? item->text() : "") << "\"";
            if (column < tableWidget->columnCount() - 1) out << ",";
        }
        out << "\n";
    }

    // 关闭文件
    file.close();

    QMessageBox::information(nullptr, "成功", "导出成功！");
}

void YRMainWindow::on_main_column_to_csv_clicked()
{
    exportTableToCSV(ui->tableWidget);
}

// 完整显示<-->实列显示
void YRMainWindow::on_blank_change_clicked(bool checked)
{
    if (checked) {
        removeEmptyColumns(true);
        ui->blank_change->setText("显示空列");
    } else {
        // 初始化模型
        if (!data_model) {
            data_model = new QStandardItemModel(this);
        }
        data_model->clear();

        // 查询TestData表 - 使用参数化查询
        QSqlQuery dataQuery(yrDatabase);
        dataQuery.prepare("SELECT * FROM TestData "
                         "WHERE TestID = ? ORDER BY id");  // 使用?占位符
        dataQuery.addBindValue(nowTestID);

        if (!dataQuery.exec()) {
            qWarning() << "TestData查询失败:" << dataQuery.lastError().text();
            return;
        }

        QSqlRecord record = dataQuery.record();
        for (int row = 0; row < dataQuery.size(); ++row) {
            if (dataQuery.seek(row)) {
                for (int i = 0; i < record.count(); ++i) {
                    data_model->setItem(row, i, new QStandardItem(dataQuery.value(i).toString()));
                }
            }
        }

        // 获取列名
        for (int i = 0; i < record.count(); ++i) {
            QString englishName = record.fieldName(i);
            QString chineseName = csvColumnNameMap.value(englishName, englishName);
            data_model->setHeaderData(i, Qt::Horizontal, chineseName);
        }

        data_model->takeColumn(1);
        data_model->takeColumn(0);

        ui->tableView_data->setModel(data_model);
        ui->tableView_data->resizeColumnsToContents();

        ui->blank_change->setText("移除空列");
    }
}

// 刷新label的selected_count
int YRMainWindow::update_count() {

    QLabel *label = this->findChild<QLabel*>("num_label");
    //QLabel *id_label = this->findChild<QLabel*>("testid_label");

    int current_count = countAllChildItems(nullptr);
    int total_num = 0;

    QSqlQuery query(yrDatabase);
    if (!query.exec("SELECT COUNT(DISTINCT TestID) AS UniqueTestIDCount FROM TestInfo")) {
        qWarning() << "SELECT COUNT(DISTINCT TestID) AS UniqueTestIDCount FROM TestInfo" << " 查询失败: " << query.lastError().text();
    }

    while (query.next()) {
        total_num = query.value(0).toInt();
    }

    // 确保找到了label
    if (label) {
        // 设置label的文本为current_count的值
        label->setText("统计：共  " + QString::number(total_num) + "次试验，查询到  " + QString::number(current_count) +"次。" );
        //id_label->setText("试验编号： " + nowTestID);
        ui->statusbar->showMessage("试验编号： " + nowTestID, 0);
    }

    return current_count;
}

int YRMainWindow::countAllChildItems(QTreeWidgetItem *parentItem) const
{
    int count = 0;
    if (parentItem) {
        // 计算当前项的子项数量
        int childCount = parentItem->childCount();
        count += childCount;

        // 递归计算每个子项的子项数量
        for (int i = 0; i < childCount; ++i) {
            count += countAllChildItems(parentItem->child(i));
        }
    } else {
        // 如果没有指定父项，从顶层项开始计算
        int topLevelItemCount = ui->treeWidget->topLevelItemCount();
        for (int i = 0; i < topLevelItemCount; ++i) {
            count += countAllChildItems(ui->treeWidget->topLevelItem(i));
        }
    }
    return count;
}

void YRMainWindow::on_action_logout_triggered()
{
    nowTestID = "";
    this->close();
    login *my_login = new login();
    my_login->setWindowTitle("森林-城镇交界域典型建筑材料的引燃和燃烧危险性数据库");
    my_login->show();
}

void YRMainWindow::on_action_exit_triggered()
{
    this->close();
}

void YRMainWindow::on_action_home_triggered()
{
    nowTestID = "";
    database_home *dw = new database_home();
    //emit valueChanged(true);
    //MainWindow *w = new MainWindow();
    //connect(this,&MainWindow::valueChanged,dw,&database_home::setValue);
    dw->setValue(true, "yr");
    this->hide();
    dw->setWindowTitle("森林-城镇交界域典型建筑材料的引燃和燃烧危险性数据库");
    dw->show();
}

QString YRMainWindow::engToChs(const QString &engName) {
    // 映射字段值（保留原逻辑）
    QMapIterator<QString, QString> i(csvBaseMap);
    while (i.hasNext())  {
        i.next();
        // 填充二级标题的中文字段 i.key().length() == fields[0].length() &&
        if (i.key() == engName.left(i.key().length()) && cleanHeaderName(engName).length() == i.key().length())  {
            return i.value();
        }
    }
    return "";
}

void YRMainWindow::on_action_dirpath_triggered()
{
    // 创建一个弹窗
    QDialog dialog(this);
    dialog.setWindowTitle(tr("设置目标文件夹路径"));
    dialog.resize(400, 150);

    // 创建布局和控件
    QVBoxLayout *layout = new QVBoxLayout(&dialog);

    QLabel *label = new QLabel(tr("当前文件夹路径："));
    QLineEdit *lineEdit = new QLineEdit();
    QPushButton *browseButton = new QPushButton(tr("选择文件夹"));
    QPushButton *saveButton = new QPushButton(tr("保存路径"));
    QHBoxLayout *buttonLayout = new QHBoxLayout();

    buttonLayout->addWidget(browseButton);
    buttonLayout->addWidget(saveButton);

    // 将控件添加到布局
    layout->addWidget(label);
    layout->addWidget(lineEdit);
    layout->addLayout(buttonLayout);

    // 读取 ini 文件中的路径并设置到文本框
    QSettings setting(CONFIGPATH, QSettings::IniFormat);
    setting.setIniCodec(QTextCodec::codecForName("UTF-8"));
    QString iniDirPath = setting.value("settings/fsdirpath").toString();
    lineEdit->setText(iniDirPath);

    // 浏览按钮的逻辑
    connect(browseButton, &QPushButton::clicked, [&]() {
        QString dirPath = QFileDialog::getExistingDirectory(
                    &dialog, tr("选择目标文件夹路径"), iniDirPath,
                    QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);

        if (!dirPath.isEmpty()) {
            lineEdit->setText(dirPath); // 更新文本框内容
        }
    });

    // 保存按钮的逻辑
    connect(saveButton, &QPushButton::clicked, [&]() {
        QString updatedDirPath = lineEdit->text();

        if (updatedDirPath.isEmpty()) {
            QMessageBox::warning(&dialog, tr("警告"), tr("文件夹路径不能为空！"));
            return;
        }

        // 将路径保存到全局变量和 ini 文件
        globalDirPath = updatedDirPath;
        setting.setValue("settings/fsdirpath", globalDirPath);

        QMessageBox::information(&dialog, tr("保存成功"), tr("文件夹路径已成功保存到配置文件！"));
        dialog.accept(); // 关闭弹窗
    });

    // 显示弹窗
    dialog.exec();
}

void YRMainWindow::showMaterialStatistics() {
    // 创建对话框
    QDialog statsDialog(this);
    statsDialog.setWindowTitle("材料统计结果");
    statsDialog.resize(600, 400);

    // 创建表格
    QTableWidget *tableWidget = new QTableWidget(&statsDialog);
    tableWidget->setColumnCount(2);
    tableWidget->setHorizontalHeaderLabels({"材料名称", "出现次数"});
    tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);

    // 查询数据
    QSqlQuery query(yrDatabase);
    QString sql = "SELECT `InfoValue` FROM `TestInfo` WHERE `InfoName` LIKE '%Material name%'";

    if (!query.exec(sql)) {
        QMessageBox::critical(this, "错误", "查询失败: " + query.lastError().text());
        return;
    }

    // 统计材料出现次数
    QMap<QString, int> materialStats;
    while (query.next()) {
        QString material = query.value(0).toString().trimmed();
        if (!material.isEmpty()) {
            materialStats[material]++;
        }
    }

    if (materialStats.isEmpty()) {
        QMessageBox::information(this, "提示", "未找到任何材料数据");
        return;
    }

    // 填充表格数据
    tableWidget->setRowCount(materialStats.size());
    int row = 0;
    for (auto it = materialStats.constBegin(); it != materialStats.constEnd(); ++it) {
        tableWidget->setItem(row, 0, new QTableWidgetItem(it.key()));
        tableWidget->setItem(row, 1, new QTableWidgetItem(QString::number(it.value())));
        row++;
    }
    tableWidget->sortItems(1, Qt::DescendingOrder);

    // 创建导出按钮
    QPushButton *exportButton = new QPushButton("导出", &statsDialog);
    connect(exportButton, &QPushButton::clicked, [&]() {
            QString fileName = QFileDialog::getSaveFileName(
                        &statsDialog,
                        "保存文件",
                        QDir::homePath() + "/材料统计结果.csv",
                        "CSV 文件 (*.csv)"
                        );

            if (!fileName.isEmpty()) {
                QFile file(fileName);
                if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
                    QMessageBox::critical(&statsDialog, "错误", "无法创建文件: " + file.errorString());
                    return;
                }

                QTextStream out(&file);

                out.setCodec("GBK"); // 使用 GBK 编码

                // 写入表头 (您原来的逻辑是正确的)
                for (int column = 0; column < tableWidget->columnCount(); ++column)
                {
                    QTableWidgetItem *headerItem = tableWidget->horizontalHeaderItem(column);
                    out << "\"" << (headerItem ? headerItem->text() : "") << "\"";
                    if (column < tableWidget->columnCount() - 1) out << ",";
                }
                out << "\n";

                // --- 修改开始 ---
                // 遍历 QTableWidget，而不是 QMap
                for (int row = 0; row < tableWidget->rowCount(); ++row) {
                    QTableWidgetItem *item0 = tableWidget->item(row, 0); // 材料名称
                    QTableWidgetItem *item1 = tableWidget->item(row, 1); // 出现次数

                    // 检查 item 是否为空指针
                    QString materialName = item0 ? item0->text() : "";
                    QString count = item1 ? item1->text() : "";

                    out << "\"" << materialName << "\"," << count << "\n";
                }
                // --- 修改结束 ---

                file.close();
                QMessageBox::information(&statsDialog, "提示", "数据成功导出到: " + fileName);
            }
        });

    // 创建关闭按钮
    QPushButton *closeButton = new QPushButton("关闭", &statsDialog);
    connect(closeButton, &QPushButton::clicked, &statsDialog, &QDialog::accept);

    // 设置布局
    QVBoxLayout *layout = new QVBoxLayout(&statsDialog);
    layout->addWidget(tableWidget);
    layout->addWidget(exportButton);
    layout->addWidget(closeButton);

    // 显示对话框
    statsDialog.exec();
}
void YRMainWindow::on_pushButton_summary_clicked()
{
    showMaterialStatistics();
}
